etlantic-fastapi 0.24.0__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.
- etlantic_fastapi-0.24.0/.gitignore +32 -0
- etlantic_fastapi-0.24.0/PKG-INFO +40 -0
- etlantic_fastapi-0.24.0/README.md +14 -0
- etlantic_fastapi-0.24.0/pyproject.toml +45 -0
- etlantic_fastapi-0.24.0/src/etlantic_fastapi/__init__.py +116 -0
- etlantic_fastapi-0.24.0/tests/test_fastapi_reference_package.py +41 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.egg-info/
|
|
6
|
+
.eggs/
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
site/
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
.env
|
|
13
|
+
|
|
14
|
+
# Tools
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
.ruff_cache/
|
|
17
|
+
.mypy_cache/
|
|
18
|
+
.coverage
|
|
19
|
+
htmlcov/
|
|
20
|
+
# macOS
|
|
21
|
+
.DS_Store
|
|
22
|
+
**/.DS_Store
|
|
23
|
+
._*
|
|
24
|
+
**/._*
|
|
25
|
+
|
|
26
|
+
examples/_file_storage_out/
|
|
27
|
+
examples/_generated_*.py
|
|
28
|
+
tmp_cli_contracts/
|
|
29
|
+
tmp_/
|
|
30
|
+
|
|
31
|
+
# Local durable workspace (reports, artifacts, schema history)
|
|
32
|
+
.etlantic/
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: etlantic-fastapi
|
|
3
|
+
Version: 0.24.0
|
|
4
|
+
Summary: Thin FastAPI reference adapter for ETLantic 0.24 authoring/service contracts.
|
|
5
|
+
Project-URL: Homepage, https://github.com/eddiethedean/etlantic
|
|
6
|
+
Project-URL: Documentation, https://etlantic.readthedocs.io/
|
|
7
|
+
Project-URL: Repository, https://github.com/eddiethedean/etlantic
|
|
8
|
+
Project-URL: Issues, https://github.com/eddiethedean/etlantic/issues
|
|
9
|
+
Author-email: Odo Matthews <odosmatthews@gmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: etlantic<0.25,>=0.24.0
|
|
21
|
+
Requires-Dist: fastapi<1,>=0.115
|
|
22
|
+
Requires-Dist: pydantic<3,>=2.12
|
|
23
|
+
Provides-Extra: test
|
|
24
|
+
Requires-Dist: httpx<1,>=0.27; extra == 'test'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# etlantic-fastapi
|
|
28
|
+
|
|
29
|
+
Thin FastAPI reference adapter for the ETLantic 0.24 authoring and service
|
|
30
|
+
contract. Not a production control plane (see 1.1 FastAPI Integration Plan).
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install etlantic-fastapi
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from etlantic_fastapi import create_reference_app
|
|
38
|
+
|
|
39
|
+
app = create_reference_app()
|
|
40
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# etlantic-fastapi
|
|
2
|
+
|
|
3
|
+
Thin FastAPI reference adapter for the ETLantic 0.24 authoring and service
|
|
4
|
+
contract. Not a production control plane (see 1.1 FastAPI Integration Plan).
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
pip install etlantic-fastapi
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
from etlantic_fastapi import create_reference_app
|
|
12
|
+
|
|
13
|
+
app = create_reference_app()
|
|
14
|
+
```
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "etlantic-fastapi"
|
|
3
|
+
version = "0.24.0"
|
|
4
|
+
description = "Thin FastAPI reference adapter for ETLantic 0.24 authoring/service contracts."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
requires-python = ">=3.11"
|
|
8
|
+
authors = [{ name = "Odo Matthews", email = "odosmatthews@gmail.com" }]
|
|
9
|
+
classifiers = [
|
|
10
|
+
"Development Status :: 4 - Beta",
|
|
11
|
+
"Intended Audience :: Developers",
|
|
12
|
+
"License :: OSI Approved :: MIT License",
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"Programming Language :: Python :: 3.11",
|
|
15
|
+
"Programming Language :: Python :: 3.12",
|
|
16
|
+
"Programming Language :: Python :: 3.13",
|
|
17
|
+
"Typing :: Typed",
|
|
18
|
+
]
|
|
19
|
+
dependencies = [
|
|
20
|
+
"etlantic>=0.24.0,<0.25",
|
|
21
|
+
"fastapi>=0.115,<1",
|
|
22
|
+
"pydantic>=2.12,<3",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.optional-dependencies]
|
|
26
|
+
test = ["httpx>=0.27,<1"]
|
|
27
|
+
|
|
28
|
+
[dependency-groups]
|
|
29
|
+
dev = [
|
|
30
|
+
"httpx>=0.27,<1",
|
|
31
|
+
"pytest>=8,<9",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://github.com/eddiethedean/etlantic"
|
|
36
|
+
Documentation = "https://etlantic.readthedocs.io/"
|
|
37
|
+
Repository = "https://github.com/eddiethedean/etlantic"
|
|
38
|
+
Issues = "https://github.com/eddiethedean/etlantic/issues"
|
|
39
|
+
|
|
40
|
+
[build-system]
|
|
41
|
+
requires = ["hatchling"]
|
|
42
|
+
build-backend = "hatchling.build"
|
|
43
|
+
|
|
44
|
+
[tool.hatch.build.targets.wheel]
|
|
45
|
+
packages = ["src/etlantic_fastapi"]
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Thin FastAPI reference adapter for ETLantic authoring/service contracts."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
|
|
9
|
+
from etlantic.service import AuthoringService, PolicyContext
|
|
10
|
+
from fastapi import FastAPI, HTTPException
|
|
11
|
+
|
|
12
|
+
__version__ = "0.24.0"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DefinitionDocument(BaseModel):
|
|
16
|
+
document: dict[str, Any]
|
|
17
|
+
idempotency_key: str | None = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class EditRequest(BaseModel):
|
|
21
|
+
command: dict[str, Any]
|
|
22
|
+
expected_token: str | None = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class RunSubmitRequest(BaseModel):
|
|
26
|
+
idempotency_key: str | None = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def create_reference_app(
|
|
30
|
+
*,
|
|
31
|
+
service: AuthoringService | None = None,
|
|
32
|
+
title: str = "ETLantic Authoring Reference",
|
|
33
|
+
version: str = "0.24.0",
|
|
34
|
+
) -> FastAPI:
|
|
35
|
+
"""Create a FastAPI app exposing the public authoring/service facade."""
|
|
36
|
+
svc = service or AuthoringService(policy=PolicyContext())
|
|
37
|
+
app = FastAPI(title=title, version=version)
|
|
38
|
+
app.state.service = svc
|
|
39
|
+
|
|
40
|
+
@app.get("/negotiation")
|
|
41
|
+
def negotiation() -> dict[str, Any]:
|
|
42
|
+
return svc.negotiation()
|
|
43
|
+
|
|
44
|
+
@app.get("/catalog")
|
|
45
|
+
def catalog(definition_id: str | None = None) -> dict[str, Any]:
|
|
46
|
+
return svc.catalog(definition_id)
|
|
47
|
+
|
|
48
|
+
@app.put("/pipelines/{definition_id}")
|
|
49
|
+
def put_pipeline(definition_id: str, body: DefinitionDocument) -> dict[str, Any]:
|
|
50
|
+
try:
|
|
51
|
+
return svc.put_definition(
|
|
52
|
+
definition_id,
|
|
53
|
+
body.document,
|
|
54
|
+
idempotency_key=body.idempotency_key,
|
|
55
|
+
)
|
|
56
|
+
except Exception as exc:
|
|
57
|
+
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
58
|
+
|
|
59
|
+
@app.get("/pipelines/{definition_id}")
|
|
60
|
+
def get_pipeline(definition_id: str) -> dict[str, Any]:
|
|
61
|
+
try:
|
|
62
|
+
return svc.get_definition(definition_id)
|
|
63
|
+
except KeyError as exc:
|
|
64
|
+
raise HTTPException(status_code=404, detail="definition not found") from exc
|
|
65
|
+
|
|
66
|
+
@app.post("/pipelines/{definition_id}/edits")
|
|
67
|
+
def edit_pipeline(definition_id: str, body: EditRequest) -> dict[str, Any]:
|
|
68
|
+
try:
|
|
69
|
+
return svc.apply_edit(
|
|
70
|
+
definition_id, body.command, expected_token=body.expected_token
|
|
71
|
+
)
|
|
72
|
+
except Exception as exc:
|
|
73
|
+
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
74
|
+
|
|
75
|
+
@app.post("/pipelines/{definition_id}/validate")
|
|
76
|
+
def validate_pipeline(definition_id: str) -> dict[str, Any]:
|
|
77
|
+
try:
|
|
78
|
+
return svc.validate(definition_id)
|
|
79
|
+
except KeyError as exc:
|
|
80
|
+
raise HTTPException(status_code=404, detail="definition not found") from exc
|
|
81
|
+
|
|
82
|
+
@app.post("/pipelines/{definition_id}/plan")
|
|
83
|
+
def plan_pipeline(definition_id: str) -> dict[str, Any]:
|
|
84
|
+
try:
|
|
85
|
+
return svc.plan(definition_id)
|
|
86
|
+
except KeyError as exc:
|
|
87
|
+
raise HTTPException(status_code=404, detail="definition not found") from exc
|
|
88
|
+
|
|
89
|
+
@app.post("/pipelines/{definition_id}/runs")
|
|
90
|
+
def submit_run(
|
|
91
|
+
definition_id: str, body: RunSubmitRequest | None = None
|
|
92
|
+
) -> dict[str, Any]:
|
|
93
|
+
body = body or RunSubmitRequest()
|
|
94
|
+
try:
|
|
95
|
+
return svc.submit_run(definition_id, idempotency_key=body.idempotency_key)
|
|
96
|
+
except KeyError as exc:
|
|
97
|
+
raise HTTPException(status_code=404, detail="definition not found") from exc
|
|
98
|
+
|
|
99
|
+
@app.get("/runs/{job_id}")
|
|
100
|
+
def run_status(job_id: str) -> dict[str, Any]:
|
|
101
|
+
try:
|
|
102
|
+
return svc.job_status(job_id)
|
|
103
|
+
except KeyError as exc:
|
|
104
|
+
raise HTTPException(status_code=404, detail="job not found") from exc
|
|
105
|
+
|
|
106
|
+
@app.post("/runs/{job_id}/cancel")
|
|
107
|
+
def cancel_run(job_id: str) -> dict[str, Any]:
|
|
108
|
+
try:
|
|
109
|
+
return svc.cancel_run(job_id)
|
|
110
|
+
except KeyError as exc:
|
|
111
|
+
raise HTTPException(status_code=404, detail="job not found") from exc
|
|
112
|
+
|
|
113
|
+
return app
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
__all__ = ["AuthoringService", "PolicyContext", "create_reference_app"]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""FastAPI reference adapter OpenAPI + service fixture (no examples import)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from etlantic.authoring import (
|
|
8
|
+
definition_from_pipeline,
|
|
9
|
+
pipeline_to_dict,
|
|
10
|
+
)
|
|
11
|
+
from etlantic.authoring.resolve import callable_registry
|
|
12
|
+
|
|
13
|
+
fastapi = pytest.importorskip("fastapi")
|
|
14
|
+
etlantic_fastapi = pytest.importorskip("etlantic_fastapi")
|
|
15
|
+
from examples.memory_customers import ( # noqa: E402
|
|
16
|
+
CustomerPipeline,
|
|
17
|
+
normalize_customers,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from etlantic_fastapi import create_reference_app # noqa: E402
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_openapi_and_service_fixture() -> None:
|
|
24
|
+
defn = definition_from_pipeline(CustomerPipeline)
|
|
25
|
+
callable_registry().register(
|
|
26
|
+
defn.transformations[0].identity, "local", normalize_customers
|
|
27
|
+
)
|
|
28
|
+
app = create_reference_app()
|
|
29
|
+
schema = app.openapi()
|
|
30
|
+
assert "/catalog" in schema["paths"]
|
|
31
|
+
assert "/pipelines/{definition_id}" in schema["paths"]
|
|
32
|
+
assert "/negotiation" in schema["paths"]
|
|
33
|
+
|
|
34
|
+
svc = app.state.service
|
|
35
|
+
assert "document_versions" in svc.negotiation()
|
|
36
|
+
assert svc.negotiation().get("run_model") == "synchronous_reference"
|
|
37
|
+
put = svc.put_definition("demo", pipeline_to_dict(defn))
|
|
38
|
+
assert put["fingerprint"]
|
|
39
|
+
assert "entries" in svc.catalog("demo")
|
|
40
|
+
assert "diagnostics" in svc.validate("demo")
|
|
41
|
+
assert "plan" in svc.plan("demo")
|