diff --git a/halfapi/app.py b/halfapi/app.py index 89a96fa..6f1ef1d 100644 --- a/halfapi/app.py +++ b/halfapi/app.py @@ -10,12 +10,10 @@ It defines the following globals : """ import logging -import time # asgi framework from starlette.applications import Starlette from starlette.authentication import UnauthenticatedUser -from starlette.middleware import Middleware from starlette.routing import Route from starlette.responses import Response, PlainTextResponse from starlette.middleware.authentication import AuthenticationMiddleware diff --git a/halfapi/cli/cli.py b/halfapi/cli/cli.py index 6c2239e..768fac9 100644 --- a/halfapi/cli/cli.py +++ b/halfapi/cli/cli.py @@ -33,5 +33,6 @@ if IS_PROJECT: from . import run elif IS_HOP_PROJECT: from . import init_hop + from . import route else: from . import init diff --git a/halfapi/cli/init_hop.py b/halfapi/cli/init_hop.py index b33fb9c..3819d7d 100644 --- a/halfapi/cli/init_hop.py +++ b/halfapi/cli/init_hop.py @@ -1,4 +1,5 @@ import os +import subprocess from configparser import ConfigParser from half_orm.model import Model @@ -28,6 +29,8 @@ def init(): 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') diff --git a/halfapi/cli/route.py b/halfapi/cli/route.py new file mode 100644 index 0000000..9a8f0b4 --- /dev/null +++ b/halfapi/cli/route.py @@ -0,0 +1,95 @@ +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_module(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')) + package_name = config.get('halfORM', '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(package_name, 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}') diff --git a/halfapi/conf.py b/halfapi/conf.py index c1fa999..0169e07 100644 --- a/halfapi/conf.py +++ b/halfapi/conf.py @@ -61,6 +61,10 @@ is_project = lambda: os.path.isfile(CONF_FILE) +ENDPOINT_TYPES = [ + 'JSON' +] + diff --git a/halfapi/sql/api.sql b/halfapi/sql/api.sql new file mode 100644 index 0000000..150c912 --- /dev/null +++ b/halfapi/sql/api.sql @@ -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);