Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b6a2d47e0 | ||
|
|
f0152fd0a8 | ||
|
|
6503601c60 |
@ -10,12 +10,10 @@ It defines the following globals :
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import time
|
|
||||||
|
|
||||||
# asgi framework
|
# asgi framework
|
||||||
from starlette.applications import Starlette
|
from starlette.applications import Starlette
|
||||||
from starlette.authentication import UnauthenticatedUser
|
from starlette.authentication import UnauthenticatedUser
|
||||||
from starlette.middleware import Middleware
|
|
||||||
from starlette.routing import Route
|
from starlette.routing import Route
|
||||||
from starlette.responses import Response, PlainTextResponse
|
from starlette.responses import Response, PlainTextResponse
|
||||||
from starlette.middleware.authentication import AuthenticationMiddleware
|
from starlette.middleware.authentication import AuthenticationMiddleware
|
||||||
|
|||||||
@ -31,6 +31,8 @@ if IS_PROJECT:
|
|||||||
from . import config
|
from . import config
|
||||||
from . import domain
|
from . import domain
|
||||||
from . import run
|
from . import run
|
||||||
|
elif IS_HOP_PROJECT:
|
||||||
|
from . import init_hop
|
||||||
|
from . import route
|
||||||
else:
|
else:
|
||||||
from . import init
|
from . import init
|
||||||
|
|||||||
36
halfapi/cli/init_hop.py
Normal file
36
halfapi/cli/init_hop.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
from configparser import ConfigParser
|
||||||
|
from half_orm.model import Model
|
||||||
|
|
||||||
|
import click
|
||||||
|
from .cli import cli
|
||||||
|
|
||||||
|
@cli.command()
|
||||||
|
def init():
|
||||||
|
"""
|
||||||
|
The "halfapi init" command for hop projects
|
||||||
|
"""
|
||||||
|
hop_conf_path = os.path.join('.hop', 'config')
|
||||||
|
config = ConfigParser()
|
||||||
|
config.read([ hop_conf_path ])
|
||||||
|
|
||||||
|
assert os.path.isdir(config.get('halfORM', 'package_name'))
|
||||||
|
|
||||||
|
model = Model(config.get('halfORM', 'package_name'))
|
||||||
|
|
||||||
|
import halfapi
|
||||||
|
halfapi_path = list(halfapi.__path__)[0]
|
||||||
|
sql_path = os.path.join(halfapi_path, 'sql', 'api.sql')
|
||||||
|
|
||||||
|
with open(sql_path, 'r') as sql_file:
|
||||||
|
for query in ''.join(sql_file.readlines()).split(';'):
|
||||||
|
if len(query.strip()) == 0:
|
||||||
|
continue
|
||||||
|
model.execute_query(query.strip())
|
||||||
|
|
||||||
|
subprocess.run(['hop', 'update', '-f'])
|
||||||
|
|
||||||
|
click.echo('halfapi schema has been initialized')
|
||||||
|
click.echo('use halfapi route command to create your first route')
|
||||||
|
click.echo('example : halfapi route add')
|
||||||
173
halfapi/cli/route.py
Normal file
173
halfapi/cli/route.py
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from configparser import ConfigParser
|
||||||
|
import importlib
|
||||||
|
import click
|
||||||
|
|
||||||
|
from half_orm.model import Model
|
||||||
|
|
||||||
|
from .cli import cli
|
||||||
|
from halfapi.lib.domain import VERBS
|
||||||
|
|
||||||
|
def get_package_name():
|
||||||
|
hop_conf_path = os.path.join('.hop', 'config')
|
||||||
|
config = ConfigParser()
|
||||||
|
config.read([ hop_conf_path ])
|
||||||
|
|
||||||
|
assert os.path.isdir(config.get('halfORM', 'package_name'))
|
||||||
|
return config.get('halfORM', 'package_name')
|
||||||
|
|
||||||
|
|
||||||
|
def get_package_module(name):
|
||||||
|
package_name = get_package_name()
|
||||||
|
|
||||||
|
if sys.path[0] != '.':
|
||||||
|
sys.path.insert(0, '.')
|
||||||
|
|
||||||
|
module = importlib.import_module(
|
||||||
|
'.'.join((
|
||||||
|
package_name,
|
||||||
|
'halfapi',
|
||||||
|
name)))
|
||||||
|
|
||||||
|
if not module:
|
||||||
|
raise Exception('Could not import {}. Please hop update -f'.format(
|
||||||
|
'.'.join((
|
||||||
|
config.get('halfORM', 'package_name'),
|
||||||
|
'halfapi',
|
||||||
|
name))))
|
||||||
|
|
||||||
|
return module
|
||||||
|
|
||||||
|
@cli.group()
|
||||||
|
def route():
|
||||||
|
pass
|
||||||
|
|
||||||
|
def endpoint_create(verb, endpoint, endpoint_type):
|
||||||
|
Endpoint_mod = get_package_module('endpoint')
|
||||||
|
Endpoint = Endpoint_mod.Endpoint
|
||||||
|
EndpointTypeDoesNotExist = Endpoint_mod.EndpointTypeDoesNotExist
|
||||||
|
|
||||||
|
try:
|
||||||
|
click.echo('Endpoint creation')
|
||||||
|
new_endpoint = Endpoint.create(
|
||||||
|
verb=verb, endpoint=endpoint, endpoint_type=endpoint_type
|
||||||
|
)
|
||||||
|
return Endpoint(**new_endpoint).path
|
||||||
|
except EndpointTypeDoesNotExist:
|
||||||
|
create_endpoint_type = click.prompt(
|
||||||
|
'The endpoint type {} does not exist. Do you want to create it?'.format(endpoint_type),
|
||||||
|
default='n',
|
||||||
|
type=click.Choice(['y', 'N'], case_sensitive=False)
|
||||||
|
)
|
||||||
|
if create_endpoint_type.lower() == 'n':
|
||||||
|
click.echo('Aborting...')
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
EndpointType_mod = get_package_module('endpoint_type')
|
||||||
|
EndpointType = EndpointType_mod.EndpointType
|
||||||
|
EndpointType.create(endpoint_type)
|
||||||
|
|
||||||
|
return endpoint_create(verb, endpoint, endpoint_type)
|
||||||
|
|
||||||
|
|
||||||
|
@click.option('--type', prompt=True, type=str, default='JSON')
|
||||||
|
@click.option('--endpoint', prompt=True, type=str)
|
||||||
|
@click.option('--verb', prompt=True, type=click.Choice(VERBS, case_sensitive=False))
|
||||||
|
@route.command()
|
||||||
|
def add(verb, endpoint, type):
|
||||||
|
"""
|
||||||
|
The "halfapi route add" command for hop projects
|
||||||
|
"""
|
||||||
|
click.echo('About to create a new route : [{}] {} -> {}'.format(verb, endpoint, type))
|
||||||
|
new_endpoint = endpoint_create(verb, endpoint, type)
|
||||||
|
click.echo(f'Created endpoint {new_endpoint}')
|
||||||
|
|
||||||
|
|
||||||
|
@route.command()
|
||||||
|
def list():
|
||||||
|
"""
|
||||||
|
The "halfapi route list" command for hop projects
|
||||||
|
"""
|
||||||
|
Endpoint_mod = get_package_module('endpoint')
|
||||||
|
Endpoint = Endpoint_mod.Endpoint
|
||||||
|
|
||||||
|
click.echo('Current routes :')
|
||||||
|
for endpoint in Endpoint().select():
|
||||||
|
elt = Endpoint(**endpoint)
|
||||||
|
click.echo(f'{elt.method}: {elt.path}')
|
||||||
|
|
||||||
|
|
||||||
|
@click.option('--target', default='./Lib/api', type=str)
|
||||||
|
@route.command()
|
||||||
|
def update(target):
|
||||||
|
"""
|
||||||
|
The "halfapi route update" command for hop projects
|
||||||
|
|
||||||
|
Creates the router tree under <target>, and add missing methods
|
||||||
|
for endpoints, that raise NotImplementedError
|
||||||
|
"""
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
package = get_package_name()
|
||||||
|
target_path = os.path.join(os.path.abspath('.'), package, target)
|
||||||
|
|
||||||
|
if not os.path.isdir(target_path):
|
||||||
|
raise Exception('Missing target path {}'.format(target_path))
|
||||||
|
|
||||||
|
click.echo('Will create router tree in {}'.format(target_path))
|
||||||
|
proceed = click.prompt(
|
||||||
|
'Proceed? [Y/n]',
|
||||||
|
default='y',
|
||||||
|
type=click.Choice(['Y', 'n'], case_sensitive=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
if proceed.lower() == 'n':
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
Endpoint_mod = get_package_module('endpoint')
|
||||||
|
Endpoint = Endpoint_mod.Endpoint
|
||||||
|
|
||||||
|
missing_methods = {}
|
||||||
|
|
||||||
|
for endpoint in Endpoint().select():
|
||||||
|
elt = Endpoint(**endpoint)
|
||||||
|
path = elt.path
|
||||||
|
stack = [target_path]
|
||||||
|
|
||||||
|
for segment in path.split('/'):
|
||||||
|
stack.append(segment)
|
||||||
|
if os.path.isdir(os.path.join(*stack)):
|
||||||
|
continue
|
||||||
|
|
||||||
|
print(f'Create {os.path.join(*stack)}')
|
||||||
|
os.mkdir(os.path.join(*stack))
|
||||||
|
sleep(.1)
|
||||||
|
|
||||||
|
endpoint_mod_path = '.'.join([package, *target.split('/')[1:], *path.split('/')[1:]])
|
||||||
|
try:
|
||||||
|
endpoint_mod = importlib.import_module(endpoint_mod_path)
|
||||||
|
if not hasattr(endpoint_mod, str(elt.method)):
|
||||||
|
if endpoint_mod.__path__[0] not in missing_methods:
|
||||||
|
missing_methods[endpoint_mod.__path__[0]] = []
|
||||||
|
|
||||||
|
missing_methods[endpoint_mod.__path__[0]].append(str(elt.method))
|
||||||
|
|
||||||
|
except Exception as exc:
|
||||||
|
print(f'Could not import {endpoint_mod_path}, may be a bug')
|
||||||
|
print(exc)
|
||||||
|
endpoint_mod_path = endpoint_mod_path.replace('.', '/')
|
||||||
|
if endpoint_mod_path not in missing_methods:
|
||||||
|
missing_methods[endpoint_mod_path] = []
|
||||||
|
|
||||||
|
missing_methods[endpoint_mod_path].append(str(elt.method))
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
for path, methods in missing_methods.items():
|
||||||
|
with open(os.path.join(path, '__init__.py'), 'a+') as f:
|
||||||
|
for method in methods:
|
||||||
|
f.write('\n'.join((
|
||||||
|
f'def {method}():',
|
||||||
|
' raise NotImplementedError\n')))
|
||||||
@ -60,6 +60,14 @@ is_project = lambda: os.path.isfile(CONF_FILE)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ENDPOINT_TYPES = [
|
||||||
|
'JSON'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
config = ConfigParser(allow_no_value=True)
|
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')
|
||||||
|
|||||||
50
halfapi/sql/api.sql
Normal file
50
halfapi/sql/api.sql
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||||
|
CREATE SCHEMA IF NOT EXISTS halfapi;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS halfapi.endpoint;
|
||||||
|
DROP TABLE IF EXISTS halfapi.parameter;
|
||||||
|
DROP TABLE IF EXISTS halfapi.segment;
|
||||||
|
DROP TABLE IF EXISTS halfapi.base_table;
|
||||||
|
|
||||||
|
CREATE TABLE halfapi.segment (
|
||||||
|
id uuid DEFAULT public.gen_random_uuid(),
|
||||||
|
fqtn text,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
name text,
|
||||||
|
parent uuid DEFAULT NULL,
|
||||||
|
UNIQUE(name, parent)
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE halfapi.segment ADD CONSTRAINT
|
||||||
|
segment_parent_fkey FOREIGN KEY (parent) REFERENCES halfapi.segment(id);
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS halfapi.type;
|
||||||
|
CREATE TABLE halfapi.type (
|
||||||
|
name text,
|
||||||
|
PRIMARY KEY (name)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE halfapi.parameter (
|
||||||
|
type text REFERENCES halfapi.type(name)
|
||||||
|
) INHERITS (halfapi.segment);
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS halfapi.endpoint_type;
|
||||||
|
CREATE TABLE halfapi.endpoint_type (
|
||||||
|
name text,
|
||||||
|
PRIMARY KEY (name)
|
||||||
|
);
|
||||||
|
|
||||||
|
DROP TYPE IF EXISTS method;
|
||||||
|
CREATE TYPE method AS ENUM ('get', 'post', 'patch', 'put', 'delete');
|
||||||
|
CREATE TABLE halfapi.endpoint (
|
||||||
|
method method,
|
||||||
|
type text,
|
||||||
|
segment uuid NOT NULL,
|
||||||
|
PRIMARY KEY (method, segment)
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE halfapi.endpoint ADD CONSTRAINT
|
||||||
|
endpoint_segment_id FOREIGN KEY (segment) REFERENCES halfapi.segment(id);
|
||||||
|
|
||||||
|
ALTER TABLE halfapi.endpoint ADD CONSTRAINT
|
||||||
|
endpoint_type_name FOREIGN KEY (type) REFERENCES halfapi.endpoint_type(name);
|
||||||
6
setup.py
6
setup.py
@ -49,7 +49,8 @@ setup(
|
|||||||
"uvicorn>=0.13,<1",
|
"uvicorn>=0.13,<1",
|
||||||
"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",
|
||||||
|
"half_orm>=0.5.0"
|
||||||
],
|
],
|
||||||
classifiers=[
|
classifiers=[
|
||||||
"Development Status :: 3 - Alpha",
|
"Development Status :: 3 - Alpha",
|
||||||
@ -67,6 +68,9 @@ setup(
|
|||||||
"pylint"
|
"pylint"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
package_data={
|
||||||
|
'halfapi': ['sql/*.sql']
|
||||||
|
},
|
||||||
entry_points={
|
entry_points={
|
||||||
"console_scripts":[
|
"console_scripts":[
|
||||||
"halfapi=halfapi.cli.cli:cli"
|
"halfapi=halfapi.cli.cli:cli"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user