[conf] use of toml for halfapi configs. re-enable possibility of multiple domains
This commit is contained in:
parent
d06857bf49
commit
dbca2f28fb
2
Pipfile
2
Pipfile
|
@ -21,6 +21,8 @@ pyjwt = ">=2.3.0,<2.4.0"
|
||||||
pyyaml = ">=5.3.1,<6"
|
pyyaml = ">=5.3.1,<6"
|
||||||
timing-asgi = ">=0.2.1,<1"
|
timing-asgi = ">=0.2.1,<1"
|
||||||
schema = ">=0.7.4,<1"
|
schema = ">=0.7.4,<1"
|
||||||
|
toml = "*"
|
||||||
|
pip = "*"
|
||||||
|
|
||||||
[scripts]
|
[scripts]
|
||||||
halfapi = "python -m halfapi"
|
halfapi = "python -m halfapi"
|
||||||
|
|
|
@ -13,7 +13,7 @@ import orjson
|
||||||
|
|
||||||
|
|
||||||
from .cli import cli
|
from .cli import cli
|
||||||
from ..conf import config, write_config, DOMAINSDICT
|
from ..conf import write_config
|
||||||
|
|
||||||
from ..lib.domain import domain_schema
|
from ..lib.domain import domain_schema
|
||||||
from ..lib.schemas import schema_dict_dom
|
from ..lib.schemas import schema_dict_dom
|
||||||
|
@ -61,13 +61,7 @@ def create_domain(domain_name: str, module_path: str):
|
||||||
os.mkdir(router_path)
|
os.mkdir(router_path)
|
||||||
create_init(router_path)
|
create_init(router_path)
|
||||||
|
|
||||||
|
# TODO: Generate config file
|
||||||
if not config.has_section('domain'):
|
|
||||||
config.add_section('domain')
|
|
||||||
|
|
||||||
config.set('domain', 'name', domain_name)
|
|
||||||
config.set('domain', 'router', module_path)
|
|
||||||
write_config()
|
|
||||||
|
|
||||||
domain_tree_create()
|
domain_tree_create()
|
||||||
"""
|
"""
|
||||||
|
@ -113,11 +107,13 @@ def list_routes(domain, m_dom):
|
||||||
def list_api_routes():
|
def list_api_routes():
|
||||||
"""
|
"""
|
||||||
Echoes the list of all active domains.
|
Echoes the list of all active domains.
|
||||||
|
|
||||||
|
TODO: Rewrite function
|
||||||
"""
|
"""
|
||||||
|
|
||||||
click.echo('# API Routes')
|
click.echo('# API Routes')
|
||||||
for domain, m_dom in DOMAINSDICT().items():
|
# for domain, m_dom in DOMAINSDICT().items():
|
||||||
list_routes(domain, m_dom)
|
# list_routes(domain, m_dom)
|
||||||
|
|
||||||
|
|
||||||
@click.option('--read',default=True, is_flag=True)
|
@click.option('--read',default=True, is_flag=True)
|
||||||
|
|
|
@ -10,9 +10,10 @@ import uvicorn
|
||||||
from .cli import cli
|
from .cli import cli
|
||||||
from .domain import list_api_routes
|
from .domain import list_api_routes
|
||||||
from ..conf import (PROJECT_NAME, HOST, PORT, SCHEMA,
|
from ..conf import (PROJECT_NAME, HOST, PORT, SCHEMA,
|
||||||
PRODUCTION, LOGLEVEL, DOMAINSDICT, CONFIG, DOMAIN, ROUTER)
|
PRODUCTION, LOGLEVEL, CONFIG)
|
||||||
from ..logging import logger
|
from ..logging import logger
|
||||||
from ..lib.schemas import schema_csv_dict
|
from ..lib.schemas import schema_csv_dict
|
||||||
|
from ..half_domain import HalfDomain
|
||||||
|
|
||||||
@click.option('--host', default=HOST)
|
@click.option('--host', default=HOST)
|
||||||
@click.option('--port', default=PORT)
|
@click.option('--port', default=PORT)
|
||||||
|
@ -26,7 +27,8 @@ from ..lib.schemas import schema_csv_dict
|
||||||
@click.argument('schema', type=click.File('r'), required=False)
|
@click.argument('schema', type=click.File('r'), required=False)
|
||||||
@click.argument('domain', required=False)
|
@click.argument('domain', required=False)
|
||||||
@cli.command()
|
@cli.command()
|
||||||
def run(host, port, reload, secret, production, loglevel, prefix, check, dryrun, schema, domain):
|
def run(host, port, reload, secret, production, loglevel, prefix, check, dryrun,
|
||||||
|
schema, domain):
|
||||||
"""
|
"""
|
||||||
The "halfapi run" command
|
The "halfapi run" command
|
||||||
"""
|
"""
|
||||||
|
@ -58,6 +60,19 @@ def run(host, port, reload, secret, production, loglevel, prefix, check, dryrun,
|
||||||
for key, val in schema_csv_dict(schema, prefix).items():
|
for key, val in schema_csv_dict(schema, prefix).items():
|
||||||
SCHEMA[key] = val
|
SCHEMA[key] = val
|
||||||
|
|
||||||
|
if domain:
|
||||||
|
# If we specify a domain to run as argument
|
||||||
|
|
||||||
|
for key in CONFIG['domain']:
|
||||||
|
# Disable all domains
|
||||||
|
CONFIG['domain'].pop(key)
|
||||||
|
|
||||||
|
# And activate the desired one, mounted without prefix
|
||||||
|
CONFIG['domain'][domain] = {
|
||||||
|
'name': domain,
|
||||||
|
'prefix': False
|
||||||
|
}
|
||||||
|
|
||||||
# list_api_routes()
|
# list_api_routes()
|
||||||
|
|
||||||
click.echo(f'uvicorn.run("halfapi.app:application"\n' \
|
click.echo(f'uvicorn.run("halfapi.app:application"\n' \
|
||||||
|
|
105
halfapi/conf.py
105
halfapi/conf.py
|
@ -10,7 +10,6 @@ It uses the following environment variables :
|
||||||
It defines the following globals :
|
It defines the following globals :
|
||||||
|
|
||||||
- PROJECT_NAME (str) - HALFAPI_PROJECT_NAME
|
- PROJECT_NAME (str) - HALFAPI_PROJECT_NAME
|
||||||
- DOMAINSDICT ({domain_name: domain_module}) - HALFAPI_DOMAIN_NAME / HALFAPI_DOMAIN_MODULE
|
|
||||||
- PRODUCTION (bool) - HALFAPI_PRODUCTION
|
- PRODUCTION (bool) - HALFAPI_PRODUCTION
|
||||||
- LOGLEVEL (string) - HALFAPI_LOGLEVEL
|
- LOGLEVEL (string) - HALFAPI_LOGLEVEL
|
||||||
- BASE_DIR (str) - HALFAPI_BASE_DIR
|
- BASE_DIR (str) - HALFAPI_BASE_DIR
|
||||||
|
@ -18,7 +17,6 @@ It defines the following globals :
|
||||||
- PORT (int) - HALFAPI_PORT
|
- PORT (int) - HALFAPI_PORT
|
||||||
- CONF_DIR (str) - HALFAPI_CONF_DIR
|
- CONF_DIR (str) - HALFAPI_CONF_DIR
|
||||||
- DRYRUN (bool) - HALFAPI_DRYRUN
|
- DRYRUN (bool) - HALFAPI_DRYRUN
|
||||||
- config (ConfigParser)
|
|
||||||
|
|
||||||
It reads the following ressource :
|
It reads the following ressource :
|
||||||
|
|
||||||
|
@ -30,43 +28,41 @@ It follows the following format :
|
||||||
name = PROJECT_NAME
|
name = PROJECT_NAME
|
||||||
halfapi_version = HALFAPI_VERSION
|
halfapi_version = HALFAPI_VERSION
|
||||||
|
|
||||||
[domains]
|
[domain.domain_name]
|
||||||
domain_name = requirements-like-url
|
name = domain_name
|
||||||
|
routers = routers
|
||||||
|
|
||||||
|
[domain.domain_name.config]
|
||||||
|
option = Argh
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from os import environ
|
from os import environ
|
||||||
import sys
|
import sys
|
||||||
from configparser import ConfigParser
|
|
||||||
import importlib
|
import importlib
|
||||||
|
import tempfile
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
import toml
|
||||||
|
|
||||||
from .lib.domain import d_domains
|
from .lib.domain import d_domains
|
||||||
from .logging import logger
|
from .logging import logger
|
||||||
|
|
||||||
|
CONFIG = {}
|
||||||
|
|
||||||
PROJECT_NAME = environ.get('HALFAPI_PROJECT_NAME') or os.path.basename(os.getcwd())
|
|
||||||
DOMAINSDICT = lambda: {}
|
|
||||||
DOMAINS = {}
|
|
||||||
PRODUCTION = True
|
PRODUCTION = True
|
||||||
LOGLEVEL = 'info'
|
LOGLEVEL = 'info'
|
||||||
HOST = '127.0.0.1'
|
|
||||||
PORT = '3000'
|
|
||||||
SECRET = ''
|
|
||||||
CONF_FILE = os.environ.get('HALFAPI_CONF_FILE', '.halfapi/config')
|
CONF_FILE = os.environ.get('HALFAPI_CONF_FILE', '.halfapi/config')
|
||||||
DRYRUN = bool(os.environ.get('HALFAPI_DRYRUN', False))
|
DRYRUN = bool(os.environ.get('HALFAPI_DRYRUN', False))
|
||||||
|
|
||||||
DOMAIN = None
|
|
||||||
ROUTER = None
|
|
||||||
SCHEMA = {}
|
SCHEMA = {}
|
||||||
|
|
||||||
config = ConfigParser(allow_no_value=True)
|
|
||||||
|
|
||||||
CONF_DIR = environ.get('HALFAPI_CONF_DIR', '/etc/half_api')
|
CONF_DIR = environ.get('HALFAPI_CONF_DIR', '/etc/half_api')
|
||||||
HALFAPI_ETC_FILE=os.path.join(
|
HALFAPI_ETC_FILE=os.path.join(
|
||||||
CONF_DIR, 'default.ini'
|
CONF_DIR, 'config'
|
||||||
)
|
)
|
||||||
|
|
||||||
HALFAPI_DOT_FILE=os.path.join(
|
HALFAPI_DOT_FILE=os.path.join(
|
||||||
os.getcwd(), '.halfapi', 'config')
|
os.getcwd(), '.halfapi', 'config')
|
||||||
|
|
||||||
|
@ -85,59 +81,65 @@ def write_config():
|
||||||
"""
|
"""
|
||||||
Writes the current config to the highest priority config file
|
Writes the current config to the highest priority config file
|
||||||
"""
|
"""
|
||||||
with open(conf_files()[-1], 'w') as halfapi_config:
|
# with open(conf_files()[-1], 'w') as halfapi_config:
|
||||||
config.write(halfapi_config)
|
# config.write(halfapi_config)
|
||||||
|
pass
|
||||||
|
|
||||||
def config_dict():
|
|
||||||
"""
|
|
||||||
The config object as a dict
|
|
||||||
"""
|
|
||||||
return {
|
|
||||||
section: dict(config.items(section))
|
|
||||||
for section in config.sections()
|
|
||||||
}
|
|
||||||
|
|
||||||
def read_config():
|
def read_config():
|
||||||
"""
|
"""
|
||||||
The highest index in "filenames" are the highest priorty
|
The highest index in "filenames" are the highest priorty
|
||||||
"""
|
"""
|
||||||
config.read(HALFAPI_CONFIG_FILES)
|
return toml.load(HALFAPI_CONFIG_FILES)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CONFIG = {}
|
CONFIG = {}
|
||||||
read_config()
|
|
||||||
|
|
||||||
PROJECT_NAME = config.get('project', 'name', fallback=PROJECT_NAME)
|
PROJECT_NAME = CONFIG.get('project', {}).get(
|
||||||
|
'name',
|
||||||
|
environ.get('HALFAPI_PROJECT_NAME', os.path.basename(os.getcwd())))
|
||||||
|
|
||||||
if len(PROJECT_NAME) == 0:
|
if len(CONFIG.get('domain', {}).keys()) == 0:
|
||||||
raise Exception('Need a project name as argument')
|
logger.info('No domains')
|
||||||
|
# logger.info('Running without domains: %s', d_domains(config) or 'empty domain dictionary')
|
||||||
|
|
||||||
DOMAINSDICT = lambda: d_domains(config)
|
|
||||||
DOMAINS = DOMAINSDICT()
|
|
||||||
if len(DOMAINS) == 0:
|
|
||||||
logger.info('Running without domains: %s', d_domains(config) or 'empty domain dictionary')
|
|
||||||
|
|
||||||
HOST = config.get('project', 'host', fallback=environ.get('HALFAPI_HOST', '127.0.0.1'))
|
# Bind
|
||||||
PORT = config.getint('project', 'port', fallback=environ.get('HALFAPI_PORT', '3000'))
|
HOST = CONFIG.get('project', {}).get(
|
||||||
|
'host',
|
||||||
|
environ.get('HALFAPI_HOST', '127.0.0.1'))
|
||||||
|
PORT = int(CONFIG.get('project', {}).get(
|
||||||
|
'port',
|
||||||
|
environ.get('HALFAPI_PORT', '3000')))
|
||||||
|
|
||||||
|
|
||||||
|
# Secret
|
||||||
|
SECRET = CONFIG.get('project', {}).get(
|
||||||
|
'secret',
|
||||||
|
environ.get('HALFAPI_SECRET'))
|
||||||
|
|
||||||
|
if not SECRET:
|
||||||
|
# TODO: Create a temporary secret
|
||||||
|
_, SECRET = tempfile.mkstemp()
|
||||||
|
with open('SECRET', 'w') as secret_file:
|
||||||
|
secret_file.write(str(uuid.uuid4()))
|
||||||
|
|
||||||
secret_path = config.get('project', 'secret', fallback=environ.get('HALFAPI_SECRET', ''))
|
|
||||||
try:
|
try:
|
||||||
with open(secret_path, 'r') as secret_file:
|
with open(SECRET, 'r') as secret_file:
|
||||||
|
|
||||||
SECRET = secret_file.read().strip()
|
|
||||||
CONFIG['secret'] = SECRET.strip()
|
CONFIG['secret'] = SECRET.strip()
|
||||||
except FileNotFoundError as exc:
|
except FileNotFoundError as exc:
|
||||||
logger.info('Running without secret file: %s', secret_path or 'no file specified')
|
logger.info('Running without secret file: %s', SECRET or 'no file specified')
|
||||||
|
|
||||||
PRODUCTION = config.getboolean('project', 'production',
|
PRODUCTION = bool(CONFIG.get('project', {}).get(
|
||||||
fallback=environ.get('HALFAPI_PROD', True))
|
'production',
|
||||||
|
environ.get('HALFAPI_PROD', True)))
|
||||||
|
|
||||||
LOGLEVEL = config.get('project', 'loglevel',
|
LOGLEVEL = CONFIG.get('project', {}).get(
|
||||||
fallback=environ.get('HALFAPI_LOGLEVEL', 'info')).lower()
|
'loglevel',
|
||||||
|
environ.get('HALFAPI_LOGLEVEL', 'info')).lower()
|
||||||
|
|
||||||
BASE_DIR = config.get('project', 'base_dir',
|
BASE_DIR = CONFIG.get('project', {}).get(
|
||||||
fallback=environ.get('HALFAPI_BASE_DIR', '.'))
|
'base_dir',
|
||||||
|
environ.get('HALFAPI_BASE_DIR', '.'))
|
||||||
|
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
'project_name': PROJECT_NAME,
|
'project_name': PROJECT_NAME,
|
||||||
|
@ -146,4 +148,5 @@ CONFIG = {
|
||||||
'host': HOST,
|
'host': HOST,
|
||||||
'port': PORT,
|
'port': PORT,
|
||||||
'dryrun': DRYRUN,
|
'dryrun': DRYRUN,
|
||||||
|
'domain': {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
import importlib
|
||||||
|
|
||||||
|
from starlette.applications import Starlette
|
||||||
|
from starlette.routing import Router
|
||||||
|
|
||||||
|
from .half_route import HalfRoute
|
||||||
|
from .lib.routes import gen_domain_routes, gen_schema_routes
|
||||||
|
from .lib.domain_middleware import DomainMiddleware
|
||||||
|
from .logging import logger
|
||||||
|
|
||||||
|
class HalfDomain(Starlette):
|
||||||
|
def __init__(self, app, domain, router=None, config={}):
|
||||||
|
self.app = app
|
||||||
|
|
||||||
|
self.m_domain = importlib.import_module(domain)
|
||||||
|
self.name = getattr('__name__', domain, domain)
|
||||||
|
|
||||||
|
if not router:
|
||||||
|
self.router = getattr('__router__', domain, '.routers')
|
||||||
|
else:
|
||||||
|
self.router = router
|
||||||
|
|
||||||
|
self.m_router = importlib.import_module(self.router, domain)
|
||||||
|
|
||||||
|
self.m_acl = importlib.import_module(f'{domain}.acl')
|
||||||
|
|
||||||
|
self.config = config
|
||||||
|
|
||||||
|
"""
|
||||||
|
if domain:
|
||||||
|
m_domain = importlib.import_module(domain)
|
||||||
|
if not router:
|
||||||
|
router = getattr('__router__', domain, '.routers')
|
||||||
|
m_domain_router = importlib.import_module(router, domain)
|
||||||
|
m_domain_acl = importlib.import_module(f'{domain}.acl')
|
||||||
|
|
||||||
|
if not(m_domain and m_domain_router and m_domain_acl):
|
||||||
|
raise Exception('Cannot import domain')
|
||||||
|
|
||||||
|
self.schema = domain_schema(m_domain)
|
||||||
|
|
||||||
|
routes = [ Route('/', JSONRoute(self.schema)) ]
|
||||||
|
"""
|
||||||
|
|
||||||
|
logger.info('HalfDomain creation %s %s', domain, config)
|
||||||
|
super().__init__(
|
||||||
|
routes=gen_domain_routes(self.m_router),
|
||||||
|
middleware=[
|
||||||
|
(DomainMiddleware,
|
||||||
|
{
|
||||||
|
'domain': self.name,
|
||||||
|
'config': self.config
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
|
@ -40,13 +40,14 @@ from .lib.domain import domain_schema_dict, NoDomainsException, domain_schema
|
||||||
from .lib.routes import gen_domain_routes, gen_schema_routes, JSONRoute
|
from .lib.routes import gen_domain_routes, gen_schema_routes, JSONRoute
|
||||||
from .lib.schemas import schema_json, get_acls
|
from .lib.schemas import schema_json, get_acls
|
||||||
from .logging import logger, config_logging
|
from .logging import logger, config_logging
|
||||||
|
from .half_domain import HalfDomain
|
||||||
from halfapi import __version__
|
from halfapi import __version__
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class HalfAPI:
|
class HalfAPI:
|
||||||
def __init__(self, config,
|
def __init__(self, config,
|
||||||
routes_dict=None):
|
d_routes=None):
|
||||||
config_logging(logging.DEBUG)
|
config_logging(logging.DEBUG)
|
||||||
|
|
||||||
SECRET = config.get('secret')
|
SECRET = config.get('secret')
|
||||||
|
@ -55,27 +56,28 @@ class HalfAPI:
|
||||||
DRYRUN = config.get('dryrun', False)
|
DRYRUN = config.get('dryrun', False)
|
||||||
|
|
||||||
self.PRODUCTION = PRODUCTION
|
self.PRODUCTION = PRODUCTION
|
||||||
self.CONFIG = CONFIG
|
self.CONFIG = config
|
||||||
self.SECRET = SECRET
|
self.SECRET = SECRET
|
||||||
|
|
||||||
self.__application = None
|
self.__application = None
|
||||||
|
|
||||||
|
|
||||||
|
# Domains
|
||||||
|
|
||||||
""" HalfAPI routes (if not PRODUCTION, includes debug routes)
|
""" HalfAPI routes (if not PRODUCTION, includes debug routes)
|
||||||
"""
|
"""
|
||||||
routes = []
|
routes = []
|
||||||
routes.append(
|
routes.append(
|
||||||
Route('/', JSONRoute({}))
|
Mount('/halfapi', routes=list(self.halfapi_routes()))
|
||||||
)
|
)
|
||||||
|
|
||||||
routes.append(
|
logger.info('Config: %s', config)
|
||||||
Mount('/halfapi', routes=list(self.routes()))
|
logger.info('Active domains: %s', config.get('domain', {}))
|
||||||
)
|
|
||||||
|
|
||||||
if routes_dict:
|
if d_routes:
|
||||||
# Mount the routes from the routes_dict argument - domain-less mode
|
# Mount the routes from the d_routes argument - domain-less mode
|
||||||
logger.info('Domain-less mode : the given schema defines the activated routes')
|
logger.info('Domain-less mode : the given schema defines the activated routes')
|
||||||
for route in gen_schema_routes(routes_dict):
|
for route in gen_schema_routes(d_routes):
|
||||||
routes.append(route)
|
routes.append(route)
|
||||||
else:
|
else:
|
||||||
"""
|
"""
|
||||||
|
@ -104,6 +106,24 @@ class HalfAPI:
|
||||||
on_startup=startup_fcts
|
on_startup=startup_fcts
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for key, domain in config.get('domain', {}).items():
|
||||||
|
dom_name = domain.get('name', key)
|
||||||
|
if domain.get('prefix', False):
|
||||||
|
path = f'/{dom_name}'
|
||||||
|
else:
|
||||||
|
path = '/'
|
||||||
|
|
||||||
|
self.__application.mount(path,
|
||||||
|
Mount('/',
|
||||||
|
HalfDomain(
|
||||||
|
self.application,
|
||||||
|
domain.get('name', key),
|
||||||
|
domain.get('router'),
|
||||||
|
config=domain.get('config', {})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.__application.add_middleware(
|
self.__application.add_middleware(
|
||||||
DomainMiddleware,
|
DomainMiddleware,
|
||||||
|
@ -139,7 +159,7 @@ class HalfAPI:
|
||||||
def application(self):
|
def application(self):
|
||||||
return self.__application
|
return self.__application
|
||||||
|
|
||||||
def routes(self):
|
def halfapi_routes(self):
|
||||||
""" Halfapi default routes
|
""" Halfapi default routes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
@ -56,9 +56,8 @@ def route_decorator(fct: FunctionType, ret_type: str = 'json') -> Coroutine:
|
||||||
fct_args['halfapi'] = {
|
fct_args['halfapi'] = {
|
||||||
'user': request.user if
|
'user': request.user if
|
||||||
'user' in request else None,
|
'user' in request else None,
|
||||||
'config': request.scope['config'],
|
'config': request.scope.get('config', {}),
|
||||||
'domain': request.scope['domain'],
|
'domain': request.scope.get('domain', 'unknown'),
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -32,8 +32,7 @@ class DomainMiddleware(BaseHTTPMiddleware):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
request.scope['domain'] = self.domain
|
request.scope['domain'] = self.domain
|
||||||
request.scope['config'] = self.config['domain_config'][self.domain] \
|
request.scope['config'] = self.config.copy()
|
||||||
if self.domain in self.config.get('domain_config', {}) else {}
|
|
||||||
|
|
||||||
response = await call_next(request)
|
response = await call_next(request)
|
||||||
|
|
||||||
|
|
|
@ -19,11 +19,11 @@ from types import ModuleType, FunctionType
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from .domain import gen_router_routes, domain_acls, route_decorator
|
from .domain import gen_router_routes, domain_acls, route_decorator, domain_schema_dict
|
||||||
from .responses import ORJSONResponse
|
from .responses import ORJSONResponse
|
||||||
from .acl import args_check
|
from .acl import args_check
|
||||||
from ..half_route import HalfRoute
|
from ..half_route import HalfRoute
|
||||||
from ..conf import DOMAINSDICT
|
from . import acl
|
||||||
|
|
||||||
from ..logging import logger
|
from ..logging import logger
|
||||||
|
|
||||||
|
@ -58,6 +58,11 @@ def gen_domain_routes(m_domain: ModuleType):
|
||||||
Returns:
|
Returns:
|
||||||
Generator(HalfRoute)
|
Generator(HalfRoute)
|
||||||
"""
|
"""
|
||||||
|
yield HalfRoute(f'/',
|
||||||
|
JSONRoute(domain_schema_dict(m_domain)),
|
||||||
|
[{'acl': acl.public}],
|
||||||
|
'GET'
|
||||||
|
)
|
||||||
for path, method, m_router, fct, params in gen_router_routes(m_domain, []):
|
for path, method, m_router, fct, params in gen_router_routes(m_domain, []):
|
||||||
yield HalfRoute(f'/{path}', fct, params, method)
|
yield HalfRoute(f'/{path}', fct, params, method)
|
||||||
|
|
||||||
|
@ -144,9 +149,11 @@ def api_routes(m_dom: ModuleType) -> Tuple[Dict, Dict]:
|
||||||
|
|
||||||
def api_acls(request):
|
def api_acls(request):
|
||||||
""" Returns the list of possible ACLs
|
""" Returns the list of possible ACLs
|
||||||
|
|
||||||
|
# TODO: Rewrite
|
||||||
"""
|
"""
|
||||||
res = {}
|
res = {}
|
||||||
domains = DOMAINSDICT()
|
domains = {}
|
||||||
doc = 'doc' in request.query_params
|
doc = 'doc' in request.query_params
|
||||||
for domain, m_domain in domains.items():
|
for domain, m_domain in domains.items():
|
||||||
res[domain] = {}
|
res[domain] = {}
|
||||||
|
|
3
setup.py
3
setup.py
|
@ -50,7 +50,8 @@ setup(
|
||||||
"orjson>=3.4.7,<4",
|
"orjson>=3.4.7,<4",
|
||||||
"pyyaml>=5.3.1,<6",
|
"pyyaml>=5.3.1,<6",
|
||||||
"timing-asgi>=0.2.1,<1",
|
"timing-asgi>=0.2.1,<1",
|
||||||
"schema>=0.7.4,<1"
|
"schema>=0.7.4,<1",
|
||||||
|
"toml>=0.7.1,<0.8"
|
||||||
],
|
],
|
||||||
classifiers=[
|
classifiers=[
|
||||||
"Development Status :: 3 - Alpha",
|
"Development Status :: 3 - Alpha",
|
||||||
|
|
|
@ -12,7 +12,7 @@ def test_run_noproject(cli_runner):
|
||||||
|
|
||||||
result = cli_runner.invoke(cli, ['run'])
|
result = cli_runner.invoke(cli, ['run'])
|
||||||
try:
|
try:
|
||||||
assert result.exit_code == 1
|
assert result.exit_code == 0
|
||||||
except AssertionError as exc:
|
except AssertionError as exc:
|
||||||
print(result.stdout)
|
print(result.stdout)
|
||||||
raise exc
|
raise exc
|
||||||
|
|
|
@ -39,7 +39,10 @@ from halfapi.lib.jwt_middleware import (
|
||||||
def dummy_domain():
|
def dummy_domain():
|
||||||
yield {
|
yield {
|
||||||
'name': 'dummy_domain',
|
'name': 'dummy_domain',
|
||||||
'router': 'dummy_domain.routers'
|
'router': 'dummy_domain.routers',
|
||||||
|
'config': {
|
||||||
|
'test': True
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -253,10 +256,10 @@ def dummy_project():
|
||||||
f'secret = {halfapi_secret}\n',
|
f'secret = {halfapi_secret}\n',
|
||||||
'port = 3050\n',
|
'port = 3050\n',
|
||||||
'loglevel = debug\n',
|
'loglevel = debug\n',
|
||||||
'[domain]\n',
|
'[domain.dummy_domain]\n',
|
||||||
f'name = {domain}\n',
|
f'name = {domain}\n',
|
||||||
'router = dummy_domain.routers\n',
|
'router = dummy_domain.routers\n',
|
||||||
f'[{domain}]\n',
|
f'[domain.dummy_domain.config]\n',
|
||||||
'test = True'
|
'test = True'
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -271,8 +274,10 @@ def application_debug(project_runner):
|
||||||
'secret':'turlututu',
|
'secret':'turlututu',
|
||||||
'production':False,
|
'production':False,
|
||||||
'domain': {
|
'domain': {
|
||||||
'name': 'test_domain',
|
'domain': {
|
||||||
'router': 'test_domain.routers'
|
'name': 'test_domain',
|
||||||
|
'router': 'test_domain.routers'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
'config':{
|
'config':{
|
||||||
'domain_config': {'test_domain': {'test': True}}
|
'domain_config': {'test_domain': {'test': True}}
|
||||||
|
@ -288,9 +293,13 @@ def application_domain(dummy_domain):
|
||||||
return HalfAPI({
|
return HalfAPI({
|
||||||
'secret':'turlututu',
|
'secret':'turlututu',
|
||||||
'production':True,
|
'production':True,
|
||||||
'domain': dummy_domain,
|
'domain': {
|
||||||
'config':{
|
'domain': {
|
||||||
'domain_config': {'dummy_domain': {'test': True}}
|
**dummy_domain,
|
||||||
|
'config': {
|
||||||
|
'test': True
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}).application
|
}).application
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ from halfapi.lib.domain import NoDomainsException
|
||||||
def test_halfapi_dummy_domain(dummy_domain):
|
def test_halfapi_dummy_domain(dummy_domain):
|
||||||
with patch('starlette.applications.Starlette') as mock:
|
with patch('starlette.applications.Starlette') as mock:
|
||||||
mock.return_value = MagicMock()
|
mock.return_value = MagicMock()
|
||||||
halfapi = HalfAPI({
|
config = {}
|
||||||
'domain': dummy_domain
|
config['domain'] = {}
|
||||||
})
|
config['domain'][dummy_domain['name']] = dummy_domain
|
||||||
|
halfapi = HalfAPI(config)
|
||||||
|
|
|
@ -5,10 +5,12 @@ from halfapi.halfapi import HalfAPI
|
||||||
|
|
||||||
class TestConf(TestCase):
|
class TestConf(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.args = {
|
self.args = {
|
||||||
'domain': {
|
'domain': {
|
||||||
'name': 'dummy_domain',
|
'dummy_domain': {
|
||||||
'router': 'dummy_domain.routers'
|
'name': 'dummy_domain',
|
||||||
|
'router': '.routers'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
|
@ -39,7 +41,6 @@ class TestConf(TestCase):
|
||||||
CONFIG,
|
CONFIG,
|
||||||
SCHEMA,
|
SCHEMA,
|
||||||
SECRET,
|
SECRET,
|
||||||
DOMAINSDICT,
|
|
||||||
PROJECT_NAME,
|
PROJECT_NAME,
|
||||||
HOST,
|
HOST,
|
||||||
PORT,
|
PORT,
|
||||||
|
@ -49,9 +50,8 @@ class TestConf(TestCase):
|
||||||
assert isinstance(CONFIG, dict)
|
assert isinstance(CONFIG, dict)
|
||||||
assert isinstance(SCHEMA, dict)
|
assert isinstance(SCHEMA, dict)
|
||||||
assert isinstance(SECRET, str)
|
assert isinstance(SECRET, str)
|
||||||
assert isinstance(DOMAINSDICT(), dict)
|
|
||||||
assert isinstance(PROJECT_NAME, str)
|
assert isinstance(PROJECT_NAME, str)
|
||||||
assert isinstance(HOST, str)
|
assert isinstance(HOST, str)
|
||||||
assert isinstance(PORT, str)
|
assert isinstance(PORT, int)
|
||||||
assert str(int(PORT)) == PORT
|
assert int(str(int(PORT))) == PORT
|
||||||
assert isinstance(CONF_DIR, str)
|
assert isinstance(CONF_DIR, str)
|
||||||
|
|
|
@ -2,6 +2,5 @@ from halfapi.halfapi import HalfAPI
|
||||||
|
|
||||||
def test_methods():
|
def test_methods():
|
||||||
assert 'application' in dir(HalfAPI)
|
assert 'application' in dir(HalfAPI)
|
||||||
assert 'routes' in dir(HalfAPI)
|
|
||||||
assert 'version' in dir(HalfAPI)
|
assert 'version' in dir(HalfAPI)
|
||||||
assert 'version_async' in dir(HalfAPI)
|
assert 'version_async' in dir(HalfAPI)
|
||||||
|
|
Loading…
Reference in New Issue