c1groupy 0.3.0__py3-none-any.whl
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.
- c1gpy/__init__.py +0 -0
- c1gpy/fastapi_project_files/.gitignore +82 -0
- c1gpy/fastapi_project_files/.pre-commit-config.yaml +43 -0
- c1gpy/fastapi_project_files/Dockerfile +47 -0
- c1gpy/fastapi_project_files/README.md.template +161 -0
- c1gpy/fastapi_project_files/docker-compose.yml +12 -0
- c1gpy/fastapi_project_files/pyproject.toml.template +21 -0
- c1gpy/fastapi_project_files/src/__init__.py +0 -0
- c1gpy/fastapi_project_files/src/api_interface.py +55 -0
- c1gpy/fastapi_project_files/src/endpoints/__init__.py +0 -0
- c1gpy/fastapi_project_files/src/endpoints/router1/__init__.py +0 -0
- c1gpy/fastapi_project_files/src/endpoints/router1/example_endpoint.py +24 -0
- c1gpy/fastapi_project_files/src/endpoints/router2/__init__.py +0 -0
- c1gpy/fastapi_project_files/src/endpoints/router2/example_endpoint.py +24 -0
- c1gpy/fastapi_project_files/src/entrypoint.sh +17 -0
- c1gpy/fastapi_project_files/src/routers/__init__.py +0 -0
- c1gpy/fastapi_project_files/src/routers/router1.py +53 -0
- c1gpy/fastapi_project_files/src/routers/router2.py +36 -0
- c1gpy/fastapi_project_files/tests/__init__.py +0 -0
- c1gpy/fastapi_project_files/tests/test_example.py +33 -0
- c1gpy/google-utils.py +659 -0
- c1gpy/init_fastapi_project.py +198 -0
- c1gpy/init_streamlit_project.py +190 -0
- c1gpy/llm.py +8 -0
- c1gpy/logging.py +246 -0
- c1gpy/streamlit_project_files/.gitignore +82 -0
- c1gpy/streamlit_project_files/.pre-commit-config.yaml +43 -0
- c1gpy/streamlit_project_files/Dockerfile +47 -0
- c1gpy/streamlit_project_files/README.md.template +106 -0
- c1gpy/streamlit_project_files/docker-compose.yml +12 -0
- c1gpy/streamlit_project_files/pyproject.toml.template +19 -0
- c1gpy/streamlit_project_files/src/app/App.py.template +21 -0
- c1gpy/streamlit_project_files/src/app/__init__.py +0 -0
- c1gpy/streamlit_project_files/src/app/pages/1_Page_1.py +9 -0
- c1gpy/streamlit_project_files/src/app/pages/2_Page_2.py +15 -0
- c1gpy/streamlit_project_files/src/app/pages/__init__.py +0 -0
- c1gpy/streamlit_project_files/src/app/pages/home.py +27 -0
- c1gpy/streamlit_project_files/src/entrypoint.sh +33 -0
- c1gpy/streamlit_project_files/tests/__init__.py +0 -0
- c1gpy/streamlit_project_files/tests/test_example.py +8 -0
- c1gpy/utils.py +207 -0
- c1groupy-0.3.0.dist-info/METADATA +604 -0
- c1groupy-0.3.0.dist-info/RECORD +46 -0
- c1groupy-0.3.0.dist-info/WHEEL +4 -0
- c1groupy-0.3.0.dist-info/entry_points.txt +4 -0
- c1groupy-0.3.0.dist-info/licenses/LICENCE +21 -0
c1gpy/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Project-specific
|
|
2
|
+
*secret*
|
|
3
|
+
*secret*/
|
|
4
|
+
*.json
|
|
5
|
+
*.xlsx
|
|
6
|
+
*.xml
|
|
7
|
+
*.csv
|
|
8
|
+
*cache*
|
|
9
|
+
*.zip
|
|
10
|
+
input/
|
|
11
|
+
data_files/
|
|
12
|
+
duckdb
|
|
13
|
+
.env
|
|
14
|
+
|
|
15
|
+
# Python
|
|
16
|
+
__pycache__/
|
|
17
|
+
*.py[cod]
|
|
18
|
+
*$py.class
|
|
19
|
+
*.so
|
|
20
|
+
.Python
|
|
21
|
+
|
|
22
|
+
# Distribution / packaging
|
|
23
|
+
build/
|
|
24
|
+
develop-eggs/
|
|
25
|
+
dist/
|
|
26
|
+
downloads/
|
|
27
|
+
eggs/
|
|
28
|
+
.eggs/
|
|
29
|
+
lib/
|
|
30
|
+
lib64/
|
|
31
|
+
parts/
|
|
32
|
+
sdist/
|
|
33
|
+
var/
|
|
34
|
+
wheels/
|
|
35
|
+
*.egg-info/
|
|
36
|
+
.installed.cfg
|
|
37
|
+
*.egg
|
|
38
|
+
MANIFEST
|
|
39
|
+
|
|
40
|
+
# Virtual environments
|
|
41
|
+
venv/
|
|
42
|
+
ENV/
|
|
43
|
+
env/
|
|
44
|
+
.venv/
|
|
45
|
+
|
|
46
|
+
# Testing
|
|
47
|
+
.pytest_cache/
|
|
48
|
+
.coverage
|
|
49
|
+
.coverage.*
|
|
50
|
+
htmlcov/
|
|
51
|
+
.tox/
|
|
52
|
+
.nox/
|
|
53
|
+
*.cover
|
|
54
|
+
.hypothesis/
|
|
55
|
+
|
|
56
|
+
# Type checking
|
|
57
|
+
.mypy_cache/
|
|
58
|
+
.dmypy.json
|
|
59
|
+
dmypy.json
|
|
60
|
+
.pytype/
|
|
61
|
+
.pyre/
|
|
62
|
+
|
|
63
|
+
# IDEs
|
|
64
|
+
.DS_Store
|
|
65
|
+
.vscode/
|
|
66
|
+
.idea/
|
|
67
|
+
*.swp
|
|
68
|
+
*.swo
|
|
69
|
+
*~
|
|
70
|
+
.project
|
|
71
|
+
.pydevproject
|
|
72
|
+
.settings/
|
|
73
|
+
|
|
74
|
+
# Jupyter Notebook
|
|
75
|
+
.ipynb_checkpoints
|
|
76
|
+
|
|
77
|
+
# Documentation
|
|
78
|
+
docs/_build/
|
|
79
|
+
site/
|
|
80
|
+
|
|
81
|
+
# Streamlit
|
|
82
|
+
.streamlit/
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# pre commit hook config
|
|
2
|
+
repos:
|
|
3
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
4
|
+
rev: v5.0.0
|
|
5
|
+
hooks:
|
|
6
|
+
- id: check-yaml
|
|
7
|
+
stages: [pre-commit]
|
|
8
|
+
- id: end-of-file-fixer
|
|
9
|
+
stages: [pre-commit]
|
|
10
|
+
- id: trailing-whitespace
|
|
11
|
+
stages: [pre-commit]
|
|
12
|
+
- id: detect-private-key
|
|
13
|
+
stages: [pre-commit]
|
|
14
|
+
- id: check-added-large-files
|
|
15
|
+
stages: [pre-commit]
|
|
16
|
+
|
|
17
|
+
- repo: https://github.com/python-poetry/poetry
|
|
18
|
+
rev: 2.1.1
|
|
19
|
+
hooks:
|
|
20
|
+
- id: poetry-check
|
|
21
|
+
stages: [pre-commit]
|
|
22
|
+
- id: poetry-lock
|
|
23
|
+
stages: [pre-commit]
|
|
24
|
+
|
|
25
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
26
|
+
# Ruff version.
|
|
27
|
+
rev: v0.11.2
|
|
28
|
+
hooks:
|
|
29
|
+
# Run the linter.
|
|
30
|
+
- id: ruff
|
|
31
|
+
args: [ --fix]
|
|
32
|
+
# Run the formatter.
|
|
33
|
+
- id: ruff-format
|
|
34
|
+
|
|
35
|
+
# pre push needs install of podman & podman-compose
|
|
36
|
+
- repo: local
|
|
37
|
+
hooks:
|
|
38
|
+
- id: run-unit-tests
|
|
39
|
+
name: Run unit tests in container
|
|
40
|
+
entry: bash -c 'PYTEST=TRUE podman-compose up --build --force-recreate --remove-orphans'
|
|
41
|
+
language: system
|
|
42
|
+
stages: [pre-push]
|
|
43
|
+
verbose: true
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
FROM python:3.12-slim AS builder
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
# Install uv
|
|
6
|
+
RUN pip install --no-cache-dir uv
|
|
7
|
+
|
|
8
|
+
# Copy only dependency files first
|
|
9
|
+
COPY pyproject.toml ./
|
|
10
|
+
|
|
11
|
+
# Install dependencies using uv
|
|
12
|
+
RUN uv pip install --system -e .
|
|
13
|
+
|
|
14
|
+
# use multi stage for smaller image size
|
|
15
|
+
FROM python:3.12-slim
|
|
16
|
+
|
|
17
|
+
LABEL author="{author_email}"
|
|
18
|
+
|
|
19
|
+
WORKDIR /app
|
|
20
|
+
|
|
21
|
+
# Copy uv and installed packages from builder
|
|
22
|
+
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
|
|
23
|
+
COPY --from=builder /usr/local/bin /usr/local/bin
|
|
24
|
+
|
|
25
|
+
# Create non-root user
|
|
26
|
+
RUN useradd -m -u 1000 appuser
|
|
27
|
+
|
|
28
|
+
# Copy project files
|
|
29
|
+
COPY pyproject.toml ./
|
|
30
|
+
COPY src/ ./src/
|
|
31
|
+
COPY tests/ ./tests/
|
|
32
|
+
|
|
33
|
+
# Change ownership of src and tests to appuser
|
|
34
|
+
RUN chown -R appuser:appuser /app/src /app/tests
|
|
35
|
+
|
|
36
|
+
# Make entrypoint executable
|
|
37
|
+
RUN chmod +x /app/src/entrypoint.sh
|
|
38
|
+
|
|
39
|
+
# Switch to non-root user
|
|
40
|
+
USER appuser
|
|
41
|
+
|
|
42
|
+
# Set default environment variables
|
|
43
|
+
ENV LOCALRUN=FALSE
|
|
44
|
+
ENV PYTEST=FALSE
|
|
45
|
+
|
|
46
|
+
#needs $PORT $LOCALRUN $PYTEST
|
|
47
|
+
ENTRYPOINT ["/app/src/entrypoint.sh"]
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# {project_name}
|
|
2
|
+
|
|
3
|
+
A FastAPI application with organized router structure.
|
|
4
|
+
|
|
5
|
+
## Project Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
{project_name}/
|
|
9
|
+
├── src/
|
|
10
|
+
│ ├── api_interface.py # Main FastAPI app
|
|
11
|
+
│ ├── routers/ # API routers
|
|
12
|
+
│ │ ├── router1.py
|
|
13
|
+
│ │ └── router2.py
|
|
14
|
+
│ ├── endpoints/ # Endpoint logic
|
|
15
|
+
│ │ ├── router1/
|
|
16
|
+
│ │ │ └── example_endpoint.py
|
|
17
|
+
│ │ └── router2/
|
|
18
|
+
│ │ └── example_endpoint.py
|
|
19
|
+
│ └── entrypoint.sh # Container entrypoint script
|
|
20
|
+
├── tests/ # Test files
|
|
21
|
+
├── Dockerfile # Docker configuration
|
|
22
|
+
├── docker-compose.yml # Docker Compose configuration
|
|
23
|
+
├── pyproject.toml # Project dependencies
|
|
24
|
+
├── .pre-commit-config.yaml # Pre-commit hooks
|
|
25
|
+
└── .gitignore # Git ignore rules
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Getting Started
|
|
29
|
+
|
|
30
|
+
### Local Development (with uv)
|
|
31
|
+
|
|
32
|
+
1. Install dependencies:
|
|
33
|
+
```bash
|
|
34
|
+
uv sync
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
2. Run the application:
|
|
38
|
+
```bash
|
|
39
|
+
uv run hypercorn src.api_interface:rest_api --bind :8000 --reload
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The API will be available at:
|
|
43
|
+
- **API**: http://localhost:8000
|
|
44
|
+
- **Interactive Docs**: http://localhost:8000/docs
|
|
45
|
+
- **ReDoc**: http://localhost:8000/redoc
|
|
46
|
+
|
|
47
|
+
### Docker Development
|
|
48
|
+
|
|
49
|
+
1. Build and run with Docker Compose:
|
|
50
|
+
```bash
|
|
51
|
+
docker-compose up --build --force-recreate --remove-orphans
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The application will be available at http://localhost:8000
|
|
55
|
+
|
|
56
|
+
The `LOCALRUN=TRUE` environment variable enables hot-reloading for development.
|
|
57
|
+
|
|
58
|
+
### Production Deployment
|
|
59
|
+
|
|
60
|
+
For production, set `LOCALRUN=FALSE` in the docker-compose.yml or use the Dockerfile directly:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
docker build -t {project_name} .
|
|
64
|
+
docker run -p 8000:8000 -e LOCALRUN=FALSE -e PORT=8000 {project_name}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Running Tests
|
|
68
|
+
|
|
69
|
+
### Locally
|
|
70
|
+
```bash
|
|
71
|
+
uv run pytest tests/
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### In Docker
|
|
75
|
+
```bash
|
|
76
|
+
docker-compose run -e PYTEST=TRUE fastapi-app
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## API Structure
|
|
80
|
+
|
|
81
|
+
### Routers
|
|
82
|
+
|
|
83
|
+
Routers are defined in `src/routers/` and organize related endpoints:
|
|
84
|
+
|
|
85
|
+
- **router1.py**: Example router with basic CRUD operations
|
|
86
|
+
- **router2.py**: Example router with data processing endpoints
|
|
87
|
+
|
|
88
|
+
### Endpoints
|
|
89
|
+
|
|
90
|
+
Endpoint logic is separated into `src/endpoints/` for better organization:
|
|
91
|
+
|
|
92
|
+
- **router1/**: Business logic for router1 endpoints
|
|
93
|
+
- **router2/**: Business logic for router2 endpoints
|
|
94
|
+
|
|
95
|
+
### Adding New Endpoints
|
|
96
|
+
|
|
97
|
+
1. Create endpoint logic in `src/endpoints/router_name/`:
|
|
98
|
+
```python
|
|
99
|
+
# src/endpoints/router1/new_endpoint.py
|
|
100
|
+
async def get_data() -> dict:
|
|
101
|
+
return {{"data": "example"}}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
2. Add route in `src/routers/router1.py`:
|
|
105
|
+
```python
|
|
106
|
+
from endpoints.router1 import new_endpoint
|
|
107
|
+
|
|
108
|
+
@router.get("/data")
|
|
109
|
+
async def get_data():
|
|
110
|
+
return await new_endpoint.get_data()
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Adding New Routers
|
|
114
|
+
|
|
115
|
+
1. Create router file in `src/routers/router3.py`:
|
|
116
|
+
```python
|
|
117
|
+
from fastapi import APIRouter
|
|
118
|
+
|
|
119
|
+
router = APIRouter(prefix="/router3", tags=["router3"])
|
|
120
|
+
|
|
121
|
+
@router.get("/example")
|
|
122
|
+
async def example():
|
|
123
|
+
return {{"message": "Hello from router3"}}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
2. Register in `src/api_interface.py`:
|
|
127
|
+
```python
|
|
128
|
+
from routers import router1, router2, router3
|
|
129
|
+
|
|
130
|
+
rest_api.include_router(router3.router)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## API Security
|
|
134
|
+
|
|
135
|
+
The template includes commented-out API key authentication:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
# Uncomment in routers to enable
|
|
139
|
+
from fastapi.security import APIKeyHeader
|
|
140
|
+
api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False)
|
|
141
|
+
|
|
142
|
+
@router.get("/secure")
|
|
143
|
+
async def secure_endpoint(api_key: str = Security(api_key_header)):
|
|
144
|
+
# Validate api_key
|
|
145
|
+
return {{"secure": "data"}}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Environment Variables
|
|
149
|
+
|
|
150
|
+
- `PORT`: Port for the API server (default: 8000)
|
|
151
|
+
- `LOCALRUN`: Set to `TRUE` for development mode with hot-reloading (default: `FALSE`)
|
|
152
|
+
- `PYTEST`: Set to `TRUE` to run tests instead of the app (default: `FALSE`)
|
|
153
|
+
|
|
154
|
+
## Pre-commit Hooks
|
|
155
|
+
|
|
156
|
+
Install pre-commit hooks:
|
|
157
|
+
```bash
|
|
158
|
+
pre-commit install
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
This will run linting and formatting checks before each commit.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "{project_name}"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A FastAPI application"
|
|
5
|
+
authors = [
|
|
6
|
+
{{email = "{author_email}"}},
|
|
7
|
+
]
|
|
8
|
+
requires-python = ">=3.12"
|
|
9
|
+
dependencies = [
|
|
10
|
+
"fastapi>=0.115.0",
|
|
11
|
+
"hypercorn[uvloop]>=0.17.0",
|
|
12
|
+
"pytest>=8.0.0",
|
|
13
|
+
"httpx>=0.27.0",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[tool.uv]
|
|
17
|
+
dev-dependencies = []
|
|
18
|
+
|
|
19
|
+
[build-system]
|
|
20
|
+
requires = ["hatchling"]
|
|
21
|
+
build-backend = "hatchling.build"
|
|
File without changes
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Main FastAPI application interface."""
|
|
2
|
+
|
|
3
|
+
from fastapi import FastAPI
|
|
4
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
5
|
+
|
|
6
|
+
from routers import router1, router2
|
|
7
|
+
|
|
8
|
+
# Initialize FastAPI app
|
|
9
|
+
rest_api = FastAPI(
|
|
10
|
+
title="{project_name}",
|
|
11
|
+
description="FastAPI application with multiple routers",
|
|
12
|
+
version="0.1.0",
|
|
13
|
+
docs_url="/docs",
|
|
14
|
+
redoc_url="/redoc",
|
|
15
|
+
contact={
|
|
16
|
+
{
|
|
17
|
+
"email": "{author_email}",
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
# Configure CORS
|
|
23
|
+
rest_api.add_middleware(
|
|
24
|
+
CORSMiddleware,
|
|
25
|
+
allow_origins=["*"], # Configure appropriately for production
|
|
26
|
+
allow_credentials=True,
|
|
27
|
+
allow_methods=["*"],
|
|
28
|
+
allow_headers=["*"],
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# Include routers
|
|
32
|
+
rest_api.include_router(router1.router)
|
|
33
|
+
rest_api.include_router(router2.router)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@rest_api.get("/")
|
|
37
|
+
async def root() -> dict:
|
|
38
|
+
"""
|
|
39
|
+
Root endpoint.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
dict: Welcome message
|
|
43
|
+
"""
|
|
44
|
+
return {{"message": "Welcome to {{project_name}}", "docs": "/docs"}}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@rest_api.get("/health")
|
|
48
|
+
async def health_check() -> dict:
|
|
49
|
+
"""
|
|
50
|
+
Health check endpoint.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
dict: Health status
|
|
54
|
+
"""
|
|
55
|
+
return {{"status": "healthy", "service": "{{project_name}}"}}
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Example endpoint for router1."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
async def get_example_data() -> dict:
|
|
5
|
+
"""
|
|
6
|
+
Example async endpoint function.
|
|
7
|
+
|
|
8
|
+
Returns:
|
|
9
|
+
dict: Example data
|
|
10
|
+
"""
|
|
11
|
+
return {"message": "Hello from router1", "status": "success"}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
async def create_example_item(item_data: dict) -> dict:
|
|
15
|
+
"""
|
|
16
|
+
Example async POST endpoint function.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
item_data: Data for the new item
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
dict: Created item with ID
|
|
23
|
+
"""
|
|
24
|
+
return {"id": 1, "data": item_data, "status": "created"}
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Example endpoint for router2."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
async def get_status() -> dict:
|
|
5
|
+
"""
|
|
6
|
+
Example async endpoint function.
|
|
7
|
+
|
|
8
|
+
Returns:
|
|
9
|
+
dict: Status information
|
|
10
|
+
"""
|
|
11
|
+
return {"status": "healthy", "service": "router2"}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
async def process_data(data: dict) -> dict:
|
|
15
|
+
"""
|
|
16
|
+
Example async data processing endpoint.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
data: Input data to process
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
dict: Processed result
|
|
23
|
+
"""
|
|
24
|
+
return {"processed": True, "input": data, "result": "processed successfully"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
## Main script for starting up FastAPI WSGI server or tests in a container
|
|
3
|
+
|
|
4
|
+
# Redirect stderr to stdout for better logging
|
|
5
|
+
exec 2>&1
|
|
6
|
+
|
|
7
|
+
if [ "${PYTEST}" = 'TRUE' ]
|
|
8
|
+
then
|
|
9
|
+
python -m pytest -v tests/ # run Pytest framework
|
|
10
|
+
else # run FastAPI
|
|
11
|
+
if [ "${LOCALRUN}" = 'TRUE' ]
|
|
12
|
+
then
|
|
13
|
+
python -m hypercorn api_interface:rest_api --bind :$PORT --reload --workers 4 --worker-class uvloop # run with reload for local dev
|
|
14
|
+
else
|
|
15
|
+
python -m hypercorn api_interface:rest_api --bind :$PORT --workers 4 --worker-class uvloop
|
|
16
|
+
fi
|
|
17
|
+
fi
|
|
File without changes
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Router1 - Example API routes."""
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter # , Security
|
|
4
|
+
# from fastapi.security import APIKeyHeader
|
|
5
|
+
|
|
6
|
+
from endpoints.router1 import example_endpoint
|
|
7
|
+
|
|
8
|
+
router = APIRouter(prefix="/router1", tags=["router1"])
|
|
9
|
+
|
|
10
|
+
# Uncomment to enable API key authentication
|
|
11
|
+
# api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@router.get("/example")
|
|
15
|
+
async def get_example() -> dict:
|
|
16
|
+
"""
|
|
17
|
+
Get example data.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
dict: Example response
|
|
21
|
+
"""
|
|
22
|
+
return await example_endpoint.get_example_data()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@router.post("/items")
|
|
26
|
+
async def create_item(item_data: dict) -> dict:
|
|
27
|
+
"""
|
|
28
|
+
Create a new item.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
item_data: Item data to create
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
dict: Created item
|
|
35
|
+
"""
|
|
36
|
+
return await example_endpoint.create_example_item(item_data)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# Example with API key security (commented out)
|
|
40
|
+
# @router.get("/secure")
|
|
41
|
+
# async def secure_endpoint(api_key: str = Security(api_key_header)):
|
|
42
|
+
# """
|
|
43
|
+
# Secure endpoint requiring API key.
|
|
44
|
+
#
|
|
45
|
+
# Args:
|
|
46
|
+
# api_key: API key from header
|
|
47
|
+
#
|
|
48
|
+
# Returns:
|
|
49
|
+
# dict: Secure data
|
|
50
|
+
# """
|
|
51
|
+
# if api_key != "your-secret-key":
|
|
52
|
+
# raise HTTPException(status_code=403, detail="Invalid API key")
|
|
53
|
+
# return {"message": "Access granted", "secure_data": "sensitive information"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Router2 - Example API routes."""
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter # , Security
|
|
4
|
+
# from fastapi.security import APIKeyHeader
|
|
5
|
+
|
|
6
|
+
from endpoints.router2 import example_endpoint
|
|
7
|
+
|
|
8
|
+
router = APIRouter(prefix="/router2", tags=["router2"])
|
|
9
|
+
|
|
10
|
+
# Uncomment to enable API key authentication
|
|
11
|
+
# api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@router.get("/status")
|
|
15
|
+
async def get_status() -> dict:
|
|
16
|
+
"""
|
|
17
|
+
Get service status.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
dict: Status information
|
|
21
|
+
"""
|
|
22
|
+
return await example_endpoint.get_status()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@router.post("/process")
|
|
26
|
+
async def process_data(data: dict) -> dict:
|
|
27
|
+
"""
|
|
28
|
+
Process input data.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
data: Data to process
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
dict: Processing result
|
|
35
|
+
"""
|
|
36
|
+
return await example_endpoint.process_data(data)
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from fastapi.testclient import TestClient
|
|
2
|
+
|
|
3
|
+
from api_interface import rest_api
|
|
4
|
+
|
|
5
|
+
client = TestClient(rest_api)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_root():
|
|
9
|
+
"""Test root endpoint."""
|
|
10
|
+
response = client.get("/")
|
|
11
|
+
assert response.status_code == 200
|
|
12
|
+
assert "message" in response.json()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_health_check():
|
|
16
|
+
"""Test health check endpoint."""
|
|
17
|
+
response = client.get("/health")
|
|
18
|
+
assert response.status_code == 200
|
|
19
|
+
assert response.json()["status"] == "healthy"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_router1_example():
|
|
23
|
+
"""Test router1 example endpoint."""
|
|
24
|
+
response = client.get("/router1/example")
|
|
25
|
+
assert response.status_code == 200
|
|
26
|
+
assert response.json()["status"] == "success"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_router2_status():
|
|
30
|
+
"""Test router2 status endpoint."""
|
|
31
|
+
response = client.get("/router2/status")
|
|
32
|
+
assert response.status_code == 200
|
|
33
|
+
assert response.json()["status"] == "healthy"
|