Compare commits
2 Commits
9bcba9c7f9
...
a6cbc002f6
Author | SHA1 | Date |
---|---|---|
Ferdinand Thiessen | a6cbc002f6 | |
Ferdinand Thiessen | 34ee95c66a |
|
@ -1,3 +1,4 @@
|
||||||
|
import io
|
||||||
import sys
|
import sys
|
||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
|
@ -93,15 +94,32 @@ class InterfaceGenerator:
|
||||||
self.basename = models.__name__
|
self.basename = models.__name__
|
||||||
self.walker(("models", models))
|
self.walker(("models", models))
|
||||||
|
|
||||||
|
def _write_types(self):
|
||||||
|
TYPE = "type {name} = {alias};\n"
|
||||||
|
INTERFACE = "interface {name} {{\n{properties}}}\n"
|
||||||
|
PROPERTY = "\t{name}{modifier}: {type};\n"
|
||||||
|
|
||||||
|
buffer = io.StringIO()
|
||||||
|
for cls, props in self.classes.items():
|
||||||
|
if isinstance(props, str):
|
||||||
|
buffer.write(TYPE.format(name=cls, alias=props))
|
||||||
|
else:
|
||||||
|
buffer.write(
|
||||||
|
INTERFACE.format(
|
||||||
|
name=cls,
|
||||||
|
properties="".join(
|
||||||
|
[PROPERTY.format(name=name, modifier=props[name][0], type=props[name][1]) for name in props]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return buffer
|
||||||
|
|
||||||
def write(self):
|
def write(self):
|
||||||
with (open(self.filename, "w") if self.filename else sys.stdout) as file:
|
with (open(self.filename, "w") if self.filename else sys.stdout) as file:
|
||||||
file.write("declare namespace {} {{\n".format(self.namespace))
|
if self.namespace:
|
||||||
for cls, params in self.classes.items():
|
file.write(f"declare namespace {self.namespace} {{\n")
|
||||||
if isinstance(params, str):
|
for line in self._write_types().getvalue().split("\n"):
|
||||||
file.write("\ttype {} = {};\n".format(cls, params))
|
file.write(f"\t{line}\n")
|
||||||
else:
|
file.write("}\n")
|
||||||
file.write("\tinterface {} {{\n".format(cls))
|
else:
|
||||||
for name in params:
|
file.write(self._write_types().getvalue())
|
||||||
file.write("\t\t{}{}: {};\n".format(name, *params[name]))
|
|
||||||
file.write("\t}\n")
|
|
||||||
file.write("}\n")
|
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
import pathlib
|
|
||||||
import subprocess
|
|
||||||
import click
|
import click
|
||||||
from os import environ
|
from flask.cli import FlaskGroup, with_appcontext
|
||||||
from flask import current_app
|
|
||||||
from flask.cli import FlaskGroup, run_command, with_appcontext
|
|
||||||
import pkg_resources
|
|
||||||
from flaschengeist.app import create_app
|
from flaschengeist.app import create_app
|
||||||
from flaschengeist.config import configure_logger
|
from flaschengeist.config import configure_logger
|
||||||
|
|
||||||
|
@ -63,98 +58,14 @@ def cli():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
def main(*args, **kwargs):
|
||||||
@with_appcontext
|
from .plugin_cmd import plugin
|
||||||
def install():
|
from .export_cmd import export
|
||||||
"""Install and initialize enabled plugins.
|
from .docs_cmd import docs
|
||||||
|
from .run_cmd import run
|
||||||
|
|
||||||
Most plugins need to install custom tables into the database
|
cli.add_command(plugin)
|
||||||
running this command will lookup all enabled plugins and run
|
cli.add_command(export)
|
||||||
their database initalization routines.
|
cli.add_command(docs)
|
||||||
"""
|
cli.add_command(run)
|
||||||
from flaschengeist.app import install_all
|
cli(*args, **kwargs)
|
||||||
|
|
||||||
install_all()
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
|
||||||
@click.option("--output", "-o", help="Output file, default is stdout", type=click.Path())
|
|
||||||
@click.option("--namespace", "-n", help="TS namespace for the interfaces", type=str, show_default=True)
|
|
||||||
@click.option("--plugin", "-p", help="Also export types for a plugin (even if disabled)", multiple=True, type=str)
|
|
||||||
@click.option("--no-core", help="Skip models / types from flaschengeist core", is_flag=True)
|
|
||||||
def export(namespace, output, no_core, plugin):
|
|
||||||
from flaschengeist import models
|
|
||||||
from flaschengeist import logger
|
|
||||||
from .InterfaceGenerator import InterfaceGenerator
|
|
||||||
|
|
||||||
gen = InterfaceGenerator(namespace, output, logger)
|
|
||||||
if not no_core:
|
|
||||||
gen.run(models)
|
|
||||||
if plugin:
|
|
||||||
for entry_point in pkg_resources.iter_entry_points("flaschengeist.plugins"):
|
|
||||||
if len(plugin) == 0 or entry_point.name in plugin:
|
|
||||||
plg = entry_point.load()
|
|
||||||
if hasattr(plg, "models") and plg.models is not None:
|
|
||||||
gen.run(plg.models)
|
|
||||||
gen.write()
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
|
||||||
@click.option("--host", help="set hostname to listen on", default="127.0.0.1", show_default=True)
|
|
||||||
@click.option("--port", help="set port to listen on", type=int, default=5000, show_default=True)
|
|
||||||
@click.option("--debug", help="run in debug mode", is_flag=True)
|
|
||||||
@with_appcontext
|
|
||||||
@click.pass_context
|
|
||||||
def run(ctx, host, port, debug):
|
|
||||||
"""Run Flaschengeist using a development server."""
|
|
||||||
|
|
||||||
class PrefixMiddleware(object):
|
|
||||||
def __init__(self, app, prefix=""):
|
|
||||||
self.app = app
|
|
||||||
self.prefix = prefix
|
|
||||||
|
|
||||||
def __call__(self, environ, start_response):
|
|
||||||
|
|
||||||
if environ["PATH_INFO"].startswith(self.prefix):
|
|
||||||
environ["PATH_INFO"] = environ["PATH_INFO"][len(self.prefix) :]
|
|
||||||
environ["SCRIPT_NAME"] = self.prefix
|
|
||||||
return self.app(environ, start_response)
|
|
||||||
else:
|
|
||||||
start_response("404", [("Content-Type", "text/plain")])
|
|
||||||
return ["This url does not belong to the app.".encode()]
|
|
||||||
|
|
||||||
from flaschengeist.config import config
|
|
||||||
|
|
||||||
# re configure logger, as we are no logger in CLI mode
|
|
||||||
configure_logger()
|
|
||||||
current_app.wsgi_app = PrefixMiddleware(current_app.wsgi_app, prefix=config["FLASCHENGEIST"].get("root", ""))
|
|
||||||
if debug:
|
|
||||||
environ["FLASK_DEBUG"] = "1"
|
|
||||||
environ["FLASK_ENV"] = "development"
|
|
||||||
|
|
||||||
ctx.invoke(run_command, host=host, port=port, debugger=debug)
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
|
||||||
@click.option(
|
|
||||||
"--output",
|
|
||||||
"-o",
|
|
||||||
help="Documentation output path",
|
|
||||||
default="./docs",
|
|
||||||
type=click.Path(file_okay=False, path_type=pathlib.Path),
|
|
||||||
)
|
|
||||||
def docs(output: pathlib.Path):
|
|
||||||
"""Generate and export API documentation using pdoc3"""
|
|
||||||
output.mkdir(parents=True, exist_ok=True)
|
|
||||||
command = [
|
|
||||||
"python",
|
|
||||||
"-m",
|
|
||||||
"pdoc",
|
|
||||||
"--skip-errors",
|
|
||||||
"--html",
|
|
||||||
"--output-dir",
|
|
||||||
str(output),
|
|
||||||
"flaschengeist",
|
|
||||||
]
|
|
||||||
click.echo(f"Running command: {command}")
|
|
||||||
subprocess.check_call(command)
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
import click
|
||||||
|
import pathlib
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.option(
|
||||||
|
"--output",
|
||||||
|
"-o",
|
||||||
|
help="Documentation output path",
|
||||||
|
default="./docs",
|
||||||
|
type=click.Path(file_okay=False, path_type=pathlib.Path),
|
||||||
|
)
|
||||||
|
@click.pass_context
|
||||||
|
def docs(ctx: click.Context, output: pathlib.Path):
|
||||||
|
"""Generate and export API documentation using pdoc"""
|
||||||
|
import pkg_resources
|
||||||
|
|
||||||
|
try:
|
||||||
|
pkg_resources.get_distribution("pdoc>=8.0.1")
|
||||||
|
except pkg_resources.DistributionNotFound:
|
||||||
|
click.echo(
|
||||||
|
f"Error: pdoc was not found, maybe you need to install it. Try:\n" "\n" '$ pip install "pdoc>=8.0.1"\n'
|
||||||
|
)
|
||||||
|
ctx.exit(1)
|
||||||
|
output.mkdir(parents=True, exist_ok=True)
|
||||||
|
command = [
|
||||||
|
"python",
|
||||||
|
"-m",
|
||||||
|
"pdoc",
|
||||||
|
"--docformat",
|
||||||
|
"google",
|
||||||
|
"--output-directory",
|
||||||
|
str(output),
|
||||||
|
"flaschengeist",
|
||||||
|
]
|
||||||
|
click.echo(f"Running command: {command}")
|
||||||
|
subprocess.check_call(command)
|
|
@ -0,0 +1,21 @@
|
||||||
|
import click
|
||||||
|
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.option("--output", "-o", help="Output file, default is stdout", type=click.Path())
|
||||||
|
@click.option("--namespace", "-n", help="TS namespace for the interfaces", type=str, show_default=True)
|
||||||
|
@click.option("--plugin", "-p", help="Also export types for a plugin (even if disabled)", multiple=True, type=str)
|
||||||
|
@click.option("--no-core", help="Skip models / types from flaschengeist core", is_flag=True)
|
||||||
|
def export(namespace, output, no_core, plugin):
|
||||||
|
from flaschengeist import logger, models
|
||||||
|
from flaschengeist.app import get_plugins
|
||||||
|
from .InterfaceGenerator import InterfaceGenerator
|
||||||
|
|
||||||
|
gen = InterfaceGenerator(namespace, output, logger)
|
||||||
|
if not no_core:
|
||||||
|
gen.run(models)
|
||||||
|
if plugin:
|
||||||
|
for plugin_class in get_plugins():
|
||||||
|
if (len(plugin) == 0 or plugin_class.id in plugin) and plugin_class.models is not None:
|
||||||
|
gen.run(plugin_class.models)
|
||||||
|
gen.write()
|
|
@ -0,0 +1,40 @@
|
||||||
|
import click
|
||||||
|
from os import environ
|
||||||
|
from flask import current_app
|
||||||
|
from flask.cli import with_appcontext, run_command
|
||||||
|
|
||||||
|
|
||||||
|
class PrefixMiddleware(object):
|
||||||
|
def __init__(self, app, prefix=""):
|
||||||
|
self.app = app
|
||||||
|
self.prefix = prefix
|
||||||
|
|
||||||
|
def __call__(self, environ, start_response):
|
||||||
|
|
||||||
|
if environ["PATH_INFO"].startswith(self.prefix):
|
||||||
|
environ["PATH_INFO"] = environ["PATH_INFO"][len(self.prefix) :]
|
||||||
|
environ["SCRIPT_NAME"] = self.prefix
|
||||||
|
return self.app(environ, start_response)
|
||||||
|
else:
|
||||||
|
start_response("404", [("Content-Type", "text/plain")])
|
||||||
|
return ["This url does not belong to the app.".encode()]
|
||||||
|
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.option("--host", help="set hostname to listen on", default="127.0.0.1", show_default=True)
|
||||||
|
@click.option("--port", help="set port to listen on", type=int, default=5000, show_default=True)
|
||||||
|
@click.option("--debug", help="run in debug mode", is_flag=True)
|
||||||
|
@with_appcontext
|
||||||
|
@click.pass_context
|
||||||
|
def run(ctx, host, port, debug):
|
||||||
|
"""Run Flaschengeist using a development server."""
|
||||||
|
from flaschengeist.config import config, configure_logger
|
||||||
|
|
||||||
|
# re configure logger, as we are no logger in CLI mode
|
||||||
|
configure_logger()
|
||||||
|
current_app.wsgi_app = PrefixMiddleware(current_app.wsgi_app, prefix=config["FLASCHENGEIST"].get("root", ""))
|
||||||
|
if debug:
|
||||||
|
environ["FLASK_DEBUG"] = "1"
|
||||||
|
environ["FLASK_ENV"] = "development"
|
||||||
|
|
||||||
|
ctx.invoke(run_command, host=host, port=port, debugger=debug)
|
|
@ -44,7 +44,7 @@ mysql =
|
||||||
|
|
||||||
[options.entry_points]
|
[options.entry_points]
|
||||||
console_scripts =
|
console_scripts =
|
||||||
flaschengeist = flaschengeist.cli:cli
|
flaschengeist = flaschengeist.cli:main
|
||||||
flask.commands =
|
flask.commands =
|
||||||
ldap = flaschengeist.plugins.auth_ldap.cli:ldap
|
ldap = flaschengeist.plugins.auth_ldap.cli:ldap
|
||||||
users = flaschengeist.plugins.users.cli:users
|
users = flaschengeist.plugins.users.cli:users
|
||||||
|
|
Loading…
Reference in New Issue