auto-rest-api 0.1.3__tar.gz → 0.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of auto-rest-api might be problematic. Click here for more details.
- {auto_rest_api-0.1.3 → auto_rest_api-0.1.4}/PKG-INFO +2 -6
- {auto_rest_api-0.1.3 → auto_rest_api-0.1.4}/README.md +1 -5
- auto_rest_api-0.1.4/auto_rest/__main__.py +70 -0
- {auto_rest_api-0.1.3 → auto_rest_api-0.1.4}/auto_rest/app.py +31 -8
- {auto_rest_api-0.1.3 → auto_rest_api-0.1.4}/auto_rest/cli.py +3 -5
- {auto_rest_api-0.1.3 → auto_rest_api-0.1.4}/auto_rest/handlers.py +1 -1
- {auto_rest_api-0.1.3 → auto_rest_api-0.1.4}/auto_rest/models.py +35 -13
- {auto_rest_api-0.1.3 → auto_rest_api-0.1.4}/auto_rest/queries.py +1 -0
- {auto_rest_api-0.1.3 → auto_rest_api-0.1.4}/auto_rest/routers.py +19 -15
- {auto_rest_api-0.1.3 → auto_rest_api-0.1.4}/pyproject.toml +1 -1
- auto_rest_api-0.1.3/auto_rest/__main__.py +0 -94
- {auto_rest_api-0.1.3 → auto_rest_api-0.1.4}/LICENSE.md +0 -0
- {auto_rest_api-0.1.3 → auto_rest_api-0.1.4}/auto_rest/__init__.py +0 -0
- {auto_rest_api-0.1.3 → auto_rest_api-0.1.4}/auto_rest/interfaces.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: auto-rest-api
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Automatically map database schemas and deploy per-table REST API endpoints.
|
|
5
5
|
License: GPL-3.0-only
|
|
6
6
|
Keywords: Better,HPC,automatic,rest,api
|
|
@@ -34,7 +34,7 @@ Description-Content-Type: text/markdown
|
|
|
34
34
|
|
|
35
35
|
# Auto-REST
|
|
36
36
|
|
|
37
|
-
A
|
|
37
|
+
A light-weight CLI tool for deploying dynamically generated REST APIs against relational databases.
|
|
38
38
|
See the [project documentation](https://better-hpc.github.io/auto-rest/) for detailed usage instructions.
|
|
39
39
|
|
|
40
40
|
## Supported Databases
|
|
@@ -70,10 +70,6 @@ Launch an API by providing connection arguments to a database of your choice.
|
|
|
70
70
|
|
|
71
71
|
```shell
|
|
72
72
|
auto-rest \
|
|
73
|
-
# Enable optional endpoints / functionality
|
|
74
|
-
--enable-docs \
|
|
75
|
-
--enable-write \
|
|
76
|
-
# Define the database type and connection arguments
|
|
77
73
|
--psql
|
|
78
74
|
--db-host localhost
|
|
79
75
|
--db-port 5432
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Auto-REST
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A light-weight CLI tool for deploying dynamically generated REST APIs against relational databases.
|
|
4
4
|
See the [project documentation](https://better-hpc.github.io/auto-rest/) for detailed usage instructions.
|
|
5
5
|
|
|
6
6
|
## Supported Databases
|
|
@@ -36,10 +36,6 @@ Launch an API by providing connection arguments to a database of your choice.
|
|
|
36
36
|
|
|
37
37
|
```shell
|
|
38
38
|
auto-rest \
|
|
39
|
-
# Enable optional endpoints / functionality
|
|
40
|
-
--enable-docs \
|
|
41
|
-
--enable-write \
|
|
42
|
-
# Define the database type and connection arguments
|
|
43
39
|
--psql
|
|
44
40
|
--db-host localhost
|
|
45
41
|
--db-port 5432
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Application entrypoint triggered by calling the packaged CLI command."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
from .app import *
|
|
6
|
+
from .cli import *
|
|
7
|
+
from .models import *
|
|
8
|
+
from .routers import *
|
|
9
|
+
|
|
10
|
+
__all__ = ["main", "run_application"]
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger("auto-rest")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def main() -> None: # pragma: no cover
|
|
16
|
+
"""Application entry point called when executing the command line interface.
|
|
17
|
+
|
|
18
|
+
This is a wrapper around the `run_application` function used to provide
|
|
19
|
+
graceful error handling.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
run_application()
|
|
24
|
+
|
|
25
|
+
except KeyboardInterrupt:
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
except Exception as e:
|
|
29
|
+
logger.critical(str(e), exc_info=True)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def run_application(cli_args: list[str] = None, /) -> None: # pragma: no cover
|
|
33
|
+
"""Run an Auto-REST API server.
|
|
34
|
+
|
|
35
|
+
This function is equivalent to launching an API server from the command line
|
|
36
|
+
and accepts the same arguments as those provided in the CLI. Arguments are
|
|
37
|
+
parsed from STDIN by default, unless specified in the function call.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
A list of commandline arguments used to run the application.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
# Parse application arguments
|
|
44
|
+
args = create_cli_parser().parse_args(cli_args)
|
|
45
|
+
configure_cli_logging(args.log_level)
|
|
46
|
+
|
|
47
|
+
logger.info(f"Resolving database connection settings.")
|
|
48
|
+
db_kwargs = parse_db_settings(args.db_config)
|
|
49
|
+
db_url = create_db_url(
|
|
50
|
+
driver=args.db_driver,
|
|
51
|
+
host=args.db_host,
|
|
52
|
+
port=args.db_port,
|
|
53
|
+
database=args.db_name,
|
|
54
|
+
username=args.db_user,
|
|
55
|
+
password=args.db_pass
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
logger.info("Mapping database schema.")
|
|
59
|
+
db_conn = create_db_engine(db_url, **db_kwargs)
|
|
60
|
+
db_meta = create_db_metadata(db_conn)
|
|
61
|
+
|
|
62
|
+
logger.info("Creating application.")
|
|
63
|
+
app = create_app(args.app_title, args.app_version)
|
|
64
|
+
app.include_router(create_welcome_router(), prefix="")
|
|
65
|
+
app.include_router(create_meta_router(db_conn, db_meta, args.app_title, args.app_version), prefix="/meta")
|
|
66
|
+
for table_name, table in db_meta.tables.items():
|
|
67
|
+
app.include_router(create_table_router(db_conn, table), prefix=f"/db/{table_name}")
|
|
68
|
+
|
|
69
|
+
logger.info(f"Launching server on http://{args.server_host}:{args.server_port}.")
|
|
70
|
+
run_server(app, args.server_host, args.server_port)
|
|
@@ -8,20 +8,42 @@ deploying Fast-API applications.
|
|
|
8
8
|
```python
|
|
9
9
|
from auto_rest.app import create_app, run_server
|
|
10
10
|
|
|
11
|
-
app = create_app(app_title="My Application", app_version="1.2.3"
|
|
11
|
+
app = create_app(app_title="My Application", app_version="1.2.3")
|
|
12
12
|
... # Add endpoints to the application here
|
|
13
13
|
run_server(app, host="127.0.0.1", port=8081)
|
|
14
14
|
```
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
+
import logging
|
|
18
|
+
|
|
17
19
|
import uvicorn
|
|
18
|
-
from fastapi import FastAPI
|
|
20
|
+
from fastapi import FastAPI, Request
|
|
19
21
|
from fastapi.middleware.cors import CORSMiddleware
|
|
22
|
+
from starlette.responses import Response
|
|
20
23
|
|
|
21
24
|
__all__ = ["create_app", "run_server"]
|
|
22
25
|
|
|
26
|
+
logger = logging.getLogger("auto-rest")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def logging_middleware(request: Request, call_next: callable) -> Response:
|
|
30
|
+
"""FastAPI middleware for the logging response status codes.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
request: The incoming HTTP request.
|
|
34
|
+
call_next: The next middleware in the middleware chain.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
The outgoing HTTP response.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
response = await call_next(request)
|
|
41
|
+
level = logging.INFO if response.status_code < 400 else logging.ERROR
|
|
42
|
+
logger.log(level, f"{request.method} ({response.status_code}) {request.client.host} - {request.url.path}")
|
|
43
|
+
return response
|
|
23
44
|
|
|
24
|
-
|
|
45
|
+
|
|
46
|
+
def create_app(app_title: str, app_version: str) -> FastAPI:
|
|
25
47
|
"""Create and configure a FastAPI application instance.
|
|
26
48
|
|
|
27
49
|
This function initializes a FastAPI app with a customizable title, version,
|
|
@@ -31,7 +53,6 @@ def create_app(app_title: str, app_version: str, enable_docs: bool) -> FastAPI:
|
|
|
31
53
|
Args:
|
|
32
54
|
app_title: The title of the FastAPI application.
|
|
33
55
|
app_version: The version of the FastAPI application.
|
|
34
|
-
enable_docs: Whether to enable the `/docs/` endpoint.
|
|
35
56
|
|
|
36
57
|
Returns:
|
|
37
58
|
FastAPI: A configured FastAPI application instance.
|
|
@@ -40,14 +61,15 @@ def create_app(app_title: str, app_version: str, enable_docs: bool) -> FastAPI:
|
|
|
40
61
|
app = FastAPI(
|
|
41
62
|
title=app_title,
|
|
42
63
|
version=app_version,
|
|
43
|
-
docs_url="/docs/"
|
|
64
|
+
docs_url="/docs/",
|
|
44
65
|
redoc_url=None,
|
|
45
66
|
)
|
|
46
67
|
|
|
68
|
+
app.middleware("http")(logging_middleware)
|
|
47
69
|
app.add_middleware(
|
|
48
70
|
CORSMiddleware,
|
|
49
|
-
allow_origins=["*"],
|
|
50
71
|
allow_credentials=True,
|
|
72
|
+
allow_origins=["*"],
|
|
51
73
|
allow_methods=["*"],
|
|
52
74
|
allow_headers=["*"],
|
|
53
75
|
)
|
|
@@ -55,7 +77,7 @@ def create_app(app_title: str, app_version: str, enable_docs: bool) -> FastAPI:
|
|
|
55
77
|
return app
|
|
56
78
|
|
|
57
79
|
|
|
58
|
-
def run_server(app: FastAPI, host: str, port: int) -> None:
|
|
80
|
+
def run_server(app: FastAPI, host: str, port: int) -> None: # pragma: no cover
|
|
59
81
|
"""Deploy a FastAPI application server.
|
|
60
82
|
|
|
61
83
|
Args:
|
|
@@ -64,4 +86,5 @@ def run_server(app: FastAPI, host: str, port: int) -> None: # pragma: no cover
|
|
|
64
86
|
port: The port number for the server to listen on.
|
|
65
87
|
"""
|
|
66
88
|
|
|
67
|
-
|
|
89
|
+
# Uvicorn overwrites its logging level when run and needs to be manually disabled here.
|
|
90
|
+
uvicorn.run(app, host=host, port=port, log_level=1000)
|
|
@@ -62,11 +62,13 @@ def configure_cli_logging(level: str) -> None:
|
|
|
62
62
|
handler.setFormatter(DefaultFormatter(fmt="%(levelprefix)s %(message)s"))
|
|
63
63
|
logging.basicConfig(
|
|
64
64
|
force=True,
|
|
65
|
-
level=level,
|
|
66
65
|
format="%(levelprefix)s %(message)s",
|
|
67
66
|
handlers=[handler],
|
|
68
67
|
)
|
|
69
68
|
|
|
69
|
+
logging.getLogger("auto-rest").setLevel(level)
|
|
70
|
+
logging.getLogger("sqlalchemy").setLevel(1000)
|
|
71
|
+
|
|
70
72
|
|
|
71
73
|
def create_cli_parser(exit_on_error: bool = True) -> ArgumentParser:
|
|
72
74
|
"""Create a command-line argument parser with preconfigured arguments.
|
|
@@ -95,10 +97,6 @@ def create_cli_parser(exit_on_error: bool = True) -> ArgumentParser:
|
|
|
95
97
|
help="Set the logging level."
|
|
96
98
|
)
|
|
97
99
|
|
|
98
|
-
features = parser.add_argument_group(title="API features")
|
|
99
|
-
features.add_argument("--enable-docs", action="store_true", help="enable the 'docs' endpoint.")
|
|
100
|
-
features.add_argument("--enable-write", action="store_true", help="enable support for write operations.")
|
|
101
|
-
|
|
102
100
|
driver = parser.add_argument_group("database type")
|
|
103
101
|
db_type = driver.add_mutually_exclusive_group(required=True)
|
|
104
102
|
db_type.add_argument("--sqlite", action="store_const", dest="db_driver", const="sqlite+aiosqlite", help="use a SQLite database driver.")
|
|
@@ -34,6 +34,7 @@ import logging
|
|
|
34
34
|
from pathlib import Path
|
|
35
35
|
from typing import Callable
|
|
36
36
|
|
|
37
|
+
import yaml
|
|
37
38
|
from sqlalchemy import create_engine, Engine, MetaData, URL
|
|
38
39
|
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, create_async_engine
|
|
39
40
|
from sqlalchemy.orm import Session
|
|
@@ -45,15 +46,34 @@ __all__ = [
|
|
|
45
46
|
"create_db_metadata",
|
|
46
47
|
"create_db_url",
|
|
47
48
|
"create_session_iterator",
|
|
49
|
+
"parse_db_settings"
|
|
48
50
|
]
|
|
49
51
|
|
|
50
|
-
logger = logging.getLogger(
|
|
52
|
+
logger = logging.getLogger("auto-rest")
|
|
51
53
|
|
|
52
54
|
# Base classes and typing objects.
|
|
53
55
|
DBEngine = Engine | AsyncEngine
|
|
54
56
|
DBSession = Session | AsyncSession
|
|
55
57
|
|
|
56
58
|
|
|
59
|
+
def parse_db_settings(path: Path | None) -> dict[str, any]:
|
|
60
|
+
"""Parse engine configuration settings from a given file path.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
path: Path to the configuration file.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Engine configuration settings.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
if path is not None:
|
|
70
|
+
logger.debug(f"Parsing engine configuration from {path}.")
|
|
71
|
+
return yaml.safe_load(path.read_text()) or dict()
|
|
72
|
+
|
|
73
|
+
logger.debug("No connection file specified.")
|
|
74
|
+
return {}
|
|
75
|
+
|
|
76
|
+
|
|
57
77
|
def create_db_url(
|
|
58
78
|
driver: str,
|
|
59
79
|
database: str,
|
|
@@ -76,21 +96,23 @@ def create_db_url(
|
|
|
76
96
|
A fully qualified database URL.
|
|
77
97
|
"""
|
|
78
98
|
|
|
79
|
-
logger.debug("Resolving database URL.")
|
|
80
|
-
|
|
81
99
|
# Handle special case where SQLite uses file paths.
|
|
82
100
|
if "sqlite" in driver:
|
|
83
101
|
path = Path(database).resolve()
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
102
|
+
url = URL.create(drivername=driver, database=str(path))
|
|
103
|
+
|
|
104
|
+
else:
|
|
105
|
+
url = URL.create(
|
|
106
|
+
drivername=driver,
|
|
107
|
+
username=username,
|
|
108
|
+
password=password,
|
|
109
|
+
host=host,
|
|
110
|
+
port=port,
|
|
111
|
+
database=database,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
logger.debug(f"Resolved URL: {url}")
|
|
115
|
+
return url
|
|
94
116
|
|
|
95
117
|
|
|
96
118
|
def create_db_engine(url: URL, **kwargs: dict[str: any]) -> DBEngine:
|
|
@@ -23,6 +23,8 @@ routers to be added directly to an API application instance.
|
|
|
23
23
|
```
|
|
24
24
|
"""
|
|
25
25
|
|
|
26
|
+
import logging
|
|
27
|
+
|
|
26
28
|
from fastapi import APIRouter
|
|
27
29
|
from sqlalchemy import MetaData, Table
|
|
28
30
|
from starlette import status
|
|
@@ -36,6 +38,8 @@ __all__ = [
|
|
|
36
38
|
"create_welcome_router",
|
|
37
39
|
]
|
|
38
40
|
|
|
41
|
+
logger = logging.getLogger("auto-rest")
|
|
42
|
+
|
|
39
43
|
|
|
40
44
|
def create_welcome_router() -> APIRouter:
|
|
41
45
|
"""Create an API router for returning a welcome message.
|
|
@@ -44,6 +48,8 @@ def create_welcome_router() -> APIRouter:
|
|
|
44
48
|
An `APIRouter` with a single route for retrieving a welcome message.
|
|
45
49
|
"""
|
|
46
50
|
|
|
51
|
+
logger.debug("Creating welcome endpoint.")
|
|
52
|
+
|
|
47
53
|
router = APIRouter()
|
|
48
54
|
router.add_api_route(
|
|
49
55
|
path="/",
|
|
@@ -71,6 +77,8 @@ def create_meta_router(engine: DBEngine, metadata: MetaData, name: str, version:
|
|
|
71
77
|
An `APIRouter` with a routes for retrieving application metadata.
|
|
72
78
|
"""
|
|
73
79
|
|
|
80
|
+
logger.debug("Creating metadata endpoints.")
|
|
81
|
+
|
|
74
82
|
router = APIRouter()
|
|
75
83
|
tags = ["Application Metadata"]
|
|
76
84
|
|
|
@@ -101,25 +109,25 @@ def create_meta_router(engine: DBEngine, metadata: MetaData, name: str, version:
|
|
|
101
109
|
return router
|
|
102
110
|
|
|
103
111
|
|
|
104
|
-
def create_table_router(engine: DBEngine, table: Table
|
|
112
|
+
def create_table_router(engine: DBEngine, table: Table) -> APIRouter:
|
|
105
113
|
"""Create an API router with endpoint handlers for a given database table.
|
|
106
114
|
|
|
107
115
|
Args:
|
|
108
116
|
engine: The SQLAlchemy engine connected to the database.
|
|
109
117
|
table: The database table to create API endpoints for.
|
|
110
|
-
writeable: Whether the router should include support for write operations.
|
|
111
118
|
|
|
112
119
|
Returns:
|
|
113
120
|
An APIRouter instance with routes for database operations on the table.
|
|
114
121
|
"""
|
|
115
122
|
|
|
123
|
+
logger.debug(f"Creating endpoints for table `{table.name}`.")
|
|
116
124
|
router = APIRouter()
|
|
117
125
|
|
|
118
126
|
# Construct path parameters from primary key columns
|
|
119
127
|
pk_columns = sorted(column.name for column in table.primary_key.columns)
|
|
120
128
|
path_params_url = "/".join(f"{{{col_name}}}" for col_name in pk_columns)
|
|
121
129
|
|
|
122
|
-
# Add
|
|
130
|
+
# Add routes for operations against the table
|
|
123
131
|
router.add_api_route(
|
|
124
132
|
path="/",
|
|
125
133
|
methods=["GET"],
|
|
@@ -129,16 +137,14 @@ def create_table_router(engine: DBEngine, table: Table, writeable: bool = False)
|
|
|
129
137
|
tags=[table.name],
|
|
130
138
|
)
|
|
131
139
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
tags=[table.name],
|
|
141
|
-
)
|
|
140
|
+
router.add_api_route(
|
|
141
|
+
path="/",
|
|
142
|
+
methods=["POST"],
|
|
143
|
+
endpoint=create_post_record_handler(engine, table),
|
|
144
|
+
status_code=status.HTTP_201_CREATED,
|
|
145
|
+
summary="Create a new record.",
|
|
146
|
+
tags=[table.name],
|
|
147
|
+
)
|
|
142
148
|
|
|
143
149
|
# Add route for read operations against individual records
|
|
144
150
|
if pk_columns:
|
|
@@ -151,8 +157,6 @@ def create_table_router(engine: DBEngine, table: Table, writeable: bool = False)
|
|
|
151
157
|
tags=[table.name],
|
|
152
158
|
)
|
|
153
159
|
|
|
154
|
-
# Add routes for write operations against individual records
|
|
155
|
-
if pk_columns and writeable:
|
|
156
160
|
router.add_api_route(
|
|
157
161
|
path=f"/{path_params_url}/",
|
|
158
162
|
methods=["PUT"],
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "auto-rest-api"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.4"
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
description = "Automatically map database schemas and deploy per-table REST API endpoints."
|
|
10
10
|
authors = [{ name = "Better HPC LLC" }]
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
"""Application entrypoint triggered by calling the packaged CLI command."""
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
|
|
6
|
-
import yaml
|
|
7
|
-
|
|
8
|
-
from .app import *
|
|
9
|
-
from .cli import *
|
|
10
|
-
from .models import *
|
|
11
|
-
from .routers import *
|
|
12
|
-
|
|
13
|
-
__all__ = ["main", "run_application"]
|
|
14
|
-
|
|
15
|
-
logger = logging.getLogger(__name__)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def main() -> None: # pragma: no cover
|
|
19
|
-
"""Parse command-line arguments and launch an API server."""
|
|
20
|
-
|
|
21
|
-
try:
|
|
22
|
-
parser = create_cli_parser()
|
|
23
|
-
args = vars(parser.parse_args())
|
|
24
|
-
log_level = args.pop("log_level")
|
|
25
|
-
|
|
26
|
-
configure_cli_logging(log_level)
|
|
27
|
-
run_application(**args)
|
|
28
|
-
|
|
29
|
-
except KeyboardInterrupt:
|
|
30
|
-
pass
|
|
31
|
-
|
|
32
|
-
except Exception as e:
|
|
33
|
-
logger.critical(str(e), exc_info=True)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def run_application(
|
|
37
|
-
enable_docs: bool,
|
|
38
|
-
enable_write: bool,
|
|
39
|
-
db_driver: str,
|
|
40
|
-
db_host: str,
|
|
41
|
-
db_port: int,
|
|
42
|
-
db_name: str,
|
|
43
|
-
db_user: str,
|
|
44
|
-
db_pass: str,
|
|
45
|
-
db_config: Path | None,
|
|
46
|
-
server_host: str,
|
|
47
|
-
server_port: int,
|
|
48
|
-
app_title: str,
|
|
49
|
-
app_version: str,
|
|
50
|
-
) -> None: # pragma: no cover
|
|
51
|
-
"""Run an Auto-REST API server.
|
|
52
|
-
|
|
53
|
-
This function is equivalent to launching an API server from the command line
|
|
54
|
-
and accepts the same arguments as those provided in the CLI.
|
|
55
|
-
|
|
56
|
-
Args:
|
|
57
|
-
enable_docs: Whether to enable the 'docs' API endpoint.
|
|
58
|
-
enable_write: Whether to enable support for write operations.
|
|
59
|
-
db_driver: SQLAlchemy-compatible database driver.
|
|
60
|
-
db_host: Database host address.
|
|
61
|
-
db_port: Database port number.
|
|
62
|
-
db_name: Database name.
|
|
63
|
-
db_user: Database authentication username.
|
|
64
|
-
db_pass: Database authentication password.
|
|
65
|
-
db_config: Path to a database configuration file.
|
|
66
|
-
server_host: API server host address.
|
|
67
|
-
server_port: API server port number.
|
|
68
|
-
app_title: title for the generated OpenAPI schema.
|
|
69
|
-
app_version: version number for the generated OpenAPI schema.
|
|
70
|
-
"""
|
|
71
|
-
|
|
72
|
-
logger.info(f"Mapping database schema for {db_name}.")
|
|
73
|
-
|
|
74
|
-
# Resolve database connection settings
|
|
75
|
-
db_url = create_db_url(driver=db_driver, host=db_host, port=db_port, database=db_name, username=db_user, password=db_pass)
|
|
76
|
-
db_kwargs = yaml.safe_load(db_config.read_text()) if db_config else {}
|
|
77
|
-
|
|
78
|
-
# Connect to and map the database.
|
|
79
|
-
db_conn = create_db_engine(db_url, **db_kwargs)
|
|
80
|
-
db_meta = create_db_metadata(db_conn)
|
|
81
|
-
|
|
82
|
-
# Build an empty application and dynamically add the requested functionality.
|
|
83
|
-
logger.info("Creating API application.")
|
|
84
|
-
app = create_app(app_title, app_version, enable_docs)
|
|
85
|
-
app.include_router(create_welcome_router(), prefix="")
|
|
86
|
-
app.include_router(create_meta_router(db_conn, db_meta, app_title, app_version), prefix="/meta")
|
|
87
|
-
|
|
88
|
-
for table_name, table in db_meta.tables.items():
|
|
89
|
-
logger.info(f"Adding `/db/{table_name}` endpoint.")
|
|
90
|
-
app.include_router(create_table_router(db_conn, table, enable_write), prefix=f"/db/{table_name}")
|
|
91
|
-
|
|
92
|
-
# Launch the API server.
|
|
93
|
-
logger.info(f"Launching API server on http://{server_host}:{server_port}.")
|
|
94
|
-
run_server(app, server_host, server_port)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|