[0.5.9] release
Squashed commit of the following: commit 7fe3e22f5e4108b5eb149abf8d608334debc49ca Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Thu Sep 2 16:59:52 2021 +0200 [0.5.9] release commit c36c0fcc982388a5acf2f9f937fa8ab54a18f3de Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Thu Sep 2 16:53:13 2021 +0200 [conf] fix #19 et ajout du test (test_dummy_project_router/test_get_config) configuration du domaine accessible depuis : l'attribut config de l'argument "halfapi" pour les fonctions request.scope['config'] pour les fonctions async commit cc235eee8c6f8f5d3606dda0f88156697eac296e Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Thu Sep 2 14:59:17 2021 +0200 [tests] don't import click two times commit fa418478c76205bb407e536737d8e389b4bf391c Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Thu Sep 2 14:57:06 2021 +0200 [clean] remove unused variables, remove [] as default value in fct, raise from exc
This commit is contained in:
parent
74b79120ba
commit
0470f9fa89
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
__version__ = '0.5.8'
|
||||
__version__ = '0.5.9'
|
||||
|
||||
def version():
|
||||
return f'HalfAPI version:{__version__}'
|
||||
|
|
|
@ -111,16 +111,15 @@ def read_config():
|
|||
The highest index in "filenames" are the highest priorty
|
||||
"""
|
||||
config.read(HALFAPI_CONFIG_FILES)
|
||||
return config_dict()
|
||||
|
||||
|
||||
|
||||
|
||||
CONFIG = {}
|
||||
IS_PROJECT = False
|
||||
if is_project():
|
||||
read_config()
|
||||
|
||||
IS_PROJECT = True
|
||||
CONFIG = read_config()
|
||||
|
||||
PROJECT_NAME = config.get('project', 'name', fallback=PROJECT_NAME)
|
||||
|
||||
|
@ -154,11 +153,11 @@ if is_project():
|
|||
'production': PRODUCTION,
|
||||
'secret': SECRET,
|
||||
'domains': DOMAINS,
|
||||
'config': {}
|
||||
'domain_config': {}
|
||||
}
|
||||
|
||||
for domain in DOMAINS:
|
||||
if domain not in config.sections():
|
||||
continue
|
||||
|
||||
CONFIG['config'][domain] = dict(config.items(domain))
|
||||
CONFIG['domain_config'][domain] = dict(config.items(domain))
|
||||
|
|
|
@ -3,16 +3,14 @@
|
|||
lib/domain.py The domain-scoped utility functions
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import importlib
|
||||
import inspect
|
||||
import logging
|
||||
from types import ModuleType, FunctionType
|
||||
from typing import Any, Callable, Coroutine, Generator
|
||||
from typing import Coroutine, Generator
|
||||
from typing import Dict, List, Tuple, Iterator
|
||||
import inspect
|
||||
|
||||
from starlette.exceptions import HTTPException
|
||||
|
||||
|
@ -51,7 +49,9 @@ def route_decorator(fct: FunctionType, ret_type: str = 'json') -> Coroutine:
|
|||
fct_args['halfapi'] = {
|
||||
'user': request.user if
|
||||
'user' in request else None,
|
||||
'config': request.scope['config']
|
||||
'config': request.scope['config'],
|
||||
'domain': request.scope['domain'],
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -65,7 +65,7 @@ def route_decorator(fct: FunctionType, ret_type: str = 'json') -> Coroutine:
|
|||
except Exception as exc:
|
||||
# TODO: Write tests
|
||||
if not isinstance(exc, HTTPException):
|
||||
raise HTTPException(500)
|
||||
raise HTTPException(500) from exc
|
||||
raise exc
|
||||
|
||||
|
||||
|
@ -200,8 +200,8 @@ def gen_router_routes(m_router: ModuleType, path: List[str]) -> \
|
|||
path.append('{{{}:{}}}'.format(
|
||||
param_match.groups()[0].lower(),
|
||||
param_match.groups()[1]))
|
||||
except AssertionError:
|
||||
raise UnknownPathParameterType(subroute)
|
||||
except AssertionError as exc:
|
||||
raise UnknownPathParameterType(subroute) from exc
|
||||
else:
|
||||
path.append(subroute)
|
||||
|
||||
|
@ -243,8 +243,6 @@ def d_domains(config) -> Dict[str, ModuleType]:
|
|||
raise exc
|
||||
|
||||
def router_acls(route_params: Dict, path: List, m_router: ModuleType) -> Generator:
|
||||
d_res = {'fqtn': route_params.get('FQTN')}
|
||||
|
||||
for verb in VERBS:
|
||||
params = route_params.get(verb)
|
||||
if params is None:
|
||||
|
@ -253,11 +251,11 @@ def router_acls(route_params: Dict, path: List, m_router: ModuleType) -> Generat
|
|||
logger.error('No ACL for route [{%s}] %s', verb, "/".join(path))
|
||||
else:
|
||||
for param in params:
|
||||
acl = param.get('acl')
|
||||
if not isinstance(acl, FunctionType):
|
||||
fct_acl = param.get('acl')
|
||||
if not isinstance(fct_acl, FunctionType):
|
||||
continue
|
||||
|
||||
yield acl.__name__, acl
|
||||
yield fct_acl.__name__, fct_acl
|
||||
|
||||
|
||||
def domain_acls(m_router, path):
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
"""
|
||||
DomainMiddleware
|
||||
"""
|
||||
import configparser
|
||||
import logging
|
||||
|
||||
from starlette.datastructures import URL
|
||||
|
@ -9,10 +8,6 @@ from starlette.middleware.base import (BaseHTTPMiddleware,
|
|||
RequestResponseEndpoint)
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response
|
||||
from starlette.types import Scope, Send, Receive
|
||||
|
||||
from .routes import api_routes
|
||||
from .domain import d_domains
|
||||
|
||||
logger = logging.getLogger('uvicorn.asgi')
|
||||
|
||||
|
@ -38,6 +33,15 @@ class DomainMiddleware(BaseHTTPMiddleware):
|
|||
Call of the route fonction (decorated or not)
|
||||
"""
|
||||
|
||||
l_path = URL(scope=request.scope).path.split('/')
|
||||
cur_domain = l_path[0]
|
||||
if len(cur_domain) == 0 and len(l_path) > 1:
|
||||
cur_domain = l_path[1]
|
||||
|
||||
request.scope['domain'] = cur_domain
|
||||
request.scope['config'] = self.config['domain_config'][cur_domain] \
|
||||
if cur_domain in self.config.get('domain_config', {}) else {}
|
||||
|
||||
response = await call_next(request)
|
||||
|
||||
if 'acl_pass' in request.scope:
|
||||
|
@ -54,5 +58,6 @@ class DomainMiddleware(BaseHTTPMiddleware):
|
|||
response.headers['x-args-optional'] = \
|
||||
','.join(request.scope['args']['optional'])
|
||||
|
||||
response.headers['x-domain'] = cur_domain
|
||||
|
||||
return response
|
||||
|
|
|
@ -24,7 +24,7 @@ from starlette.routing import Route
|
|||
from starlette.requests import Request
|
||||
from starlette.responses import Response, PlainTextResponse
|
||||
|
||||
from halfapi.lib.domain import gen_router_routes, VERBS, domain_acls
|
||||
from halfapi.lib.domain import gen_router_routes, domain_acls
|
||||
from ..conf import DOMAINSDICT
|
||||
|
||||
|
||||
|
@ -34,7 +34,7 @@ class DomainNotFoundError(Exception):
|
|||
""" Exception when a domain is not importable
|
||||
"""
|
||||
|
||||
def route_acl_decorator(fct: Callable = None, params: List[Dict] = []):
|
||||
def route_acl_decorator(fct: Callable = None, params: List[Dict] = None):
|
||||
"""
|
||||
Decorator for async functions that calls pre-conditions functions
|
||||
and appends kwargs to the target function
|
||||
|
@ -51,6 +51,9 @@ def route_acl_decorator(fct: Callable = None, params: List[Dict] = []):
|
|||
async function
|
||||
"""
|
||||
|
||||
if not params:
|
||||
params = []
|
||||
|
||||
if not fct:
|
||||
return partial(route_acl_decorator, params=params)
|
||||
|
||||
|
@ -147,7 +150,7 @@ def api_routes(m_dom: ModuleType) -> Tuple[Dict, Dict]:
|
|||
return l_params
|
||||
|
||||
d_res = {}
|
||||
for path, verb, fct, params in gen_router_routes(m_dom, []):
|
||||
for path, verb, _, params in gen_router_routes(m_dom, []):
|
||||
if path not in d_res:
|
||||
d_res[path] = {}
|
||||
d_res[path][verb] = str_acl(params)
|
||||
|
|
|
@ -98,7 +98,7 @@ async def schema_json(request, *args, **kwargs):
|
|||
SCHEMAS.get_schema(routes=request.app.routes))
|
||||
|
||||
|
||||
def schema_dict_dom(d_domains) -> Dict:
|
||||
def schema_dict_dom(d_domains: Dict[str, ModuleType]) -> Dict:
|
||||
"""
|
||||
Returns the API schema of the *m_domain* domain as a python dictionnary
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ import importlib
|
|||
import tempfile
|
||||
from typing import Dict, Tuple
|
||||
from uuid import uuid1, uuid4, UUID
|
||||
import click
|
||||
from click.testing import CliRunner
|
||||
import jwt
|
||||
import sys
|
||||
|
@ -70,7 +69,7 @@ def runner():
|
|||
@pytest.fixture
|
||||
def cli_runner():
|
||||
"""Yield a click.testing.CliRunner to invoke the CLI."""
|
||||
class_ = click.testing.CliRunner
|
||||
class_ = CliRunner
|
||||
|
||||
def invoke_wrapper(f):
|
||||
"""Augment CliRunner.invoke to emit its output to stdout.
|
||||
|
@ -266,7 +265,9 @@ def dummy_project():
|
|||
'port = 3050\n',
|
||||
'loglevel = debug\n',
|
||||
'[domains]\n',
|
||||
f'{domain}= .routers'
|
||||
f'{domain} = .routers',
|
||||
f'[{domain}]',
|
||||
'test = True'
|
||||
])
|
||||
|
||||
with open(halfapi_secret, 'w') as f:
|
||||
|
@ -297,7 +298,11 @@ def application_domain(routers):
|
|||
return HalfAPI({
|
||||
'SECRET':'turlututu',
|
||||
'PRODUCTION':True,
|
||||
'DOMAINS':{'dummy_domain':routers}
|
||||
'DOMAINS':{'dummy_domain':routers},
|
||||
'CONFIG':{
|
||||
'domains': {'dummy_domain':routers},
|
||||
'domain_config': {'dummy_domain': {'test': True}}
|
||||
}
|
||||
}).application
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
from halfapi.lib import acl
|
||||
import logging
|
||||
logger = logging.getLogger('uvicorn.asgi')
|
||||
|
||||
ACLS = {
|
||||
'GET' : [{'acl':acl.public}]
|
||||
}
|
||||
|
||||
def get(halfapi):
|
||||
logger.error('%s', halfapi)
|
||||
return halfapi['config']
|
|
@ -9,6 +9,10 @@ from starlette.testclient import TestClient
|
|||
|
||||
from halfapi.lib.domain import gen_router_routes
|
||||
|
||||
def test_get_config_route(dummy_project, application_domain, routers):
|
||||
c = TestClient(application_domain)
|
||||
r = c.get('/dummy_domain/config')
|
||||
assert 'test' in r.json()
|
||||
|
||||
def test_get_route(dummy_project, application_domain, routers):
|
||||
c = TestClient(application_domain)
|
||||
|
|
Loading…
Reference in New Issue