Initial commit
This commit is contained in:
+144
@@ -0,0 +1,144 @@
|
|||||||
|
### Python template
|
||||||
|
|
||||||
|
deploy/
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
.git/
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
tab_width = 4
|
||||||
|
end_of_line = lf
|
||||||
|
max_line_length = 88
|
||||||
|
ij_visual_guides = 88
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.{js,py,html}]
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.{yml,yaml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
[.flake8]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.py]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
ij_python_from_import_parentheses_force_if_multiline = true
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
name: Testing zangramru
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
cmd:
|
||||||
|
- ruff-format
|
||||||
|
- ruff
|
||||||
|
- mypy
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: '3.12'
|
||||||
|
- name: Set up UV
|
||||||
|
uses: astral-sh/setup-uv@v6
|
||||||
|
- name: Install deps
|
||||||
|
run: uv sync
|
||||||
|
- name: Run lint check
|
||||||
|
run: uv run pre-commit run -a ${{ matrix.cmd }}
|
||||||
|
pytest:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Create .env
|
||||||
|
run: touch .env
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: '3.12'
|
||||||
|
- name: Update docker-compose
|
||||||
|
uses: KengoTODA/actions-setup-docker-compose@v1
|
||||||
|
with:
|
||||||
|
version: "2.28.0"
|
||||||
|
- name: run tests
|
||||||
|
run: docker-compose run --rm api pytest -vv
|
||||||
+142
@@ -0,0 +1,142 @@
|
|||||||
|
### Python template
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
*.sqlite3
|
||||||
|
*.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
# See https://pre-commit.com for more information
|
||||||
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
|
repos:
|
||||||
|
- repo: local
|
||||||
|
hooks:
|
||||||
|
|
||||||
|
- id: ruff-format
|
||||||
|
name: Format with Ruff
|
||||||
|
entry: uv run ruff format
|
||||||
|
language: system
|
||||||
|
pass_filenames: false
|
||||||
|
always_run: true
|
||||||
|
|
||||||
|
- id: ruff
|
||||||
|
name: Check with Ruff
|
||||||
|
entry: uv run ruff
|
||||||
|
language: system
|
||||||
|
pass_filenames: false
|
||||||
|
always_run: true
|
||||||
|
args: ["check", "zangramru", "tests", "--fix"]
|
||||||
|
|
||||||
|
- id: mypy
|
||||||
|
name: Validate types with MyPy
|
||||||
|
entry: uv run mypy
|
||||||
|
language: system
|
||||||
|
types: [python]
|
||||||
|
pass_filenames: false
|
||||||
|
args:
|
||||||
|
- "zangramru"
|
||||||
|
- "tests"
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
FROM ghcr.io/astral-sh/uv:0.11.7-python3.13-trixie AS uv
|
||||||
|
|
||||||
|
# -----------------------------------
|
||||||
|
# STAGE 1: prod stage
|
||||||
|
# Only install main dependencies
|
||||||
|
# -----------------------------------
|
||||||
|
FROM python:3.13-slim-trixie AS prod
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
gcc \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ENV UV_COMPILE_BYTECODE=1
|
||||||
|
ENV UV_LINK_MODE=copy
|
||||||
|
ENV UV_PROJECT_ENVIRONMENT=/usr/local
|
||||||
|
ENV UV_PYTHON_DOWNLOADS=never
|
||||||
|
ENV UV_NO_MANAGED_PYTHON=1
|
||||||
|
ENV PICCOLO_CONF="zangramru.piccolo_conf"
|
||||||
|
|
||||||
|
WORKDIR /app/src
|
||||||
|
|
||||||
|
RUN --mount=from=uv,source=/usr/local/bin/uv,target=/bin/uv \
|
||||||
|
--mount=type=cache,target=/root/.cache/uv \
|
||||||
|
--mount=type=bind,source=uv.lock,target=uv.lock \
|
||||||
|
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
||||||
|
uv sync --locked --no-install-project --no-dev
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN --mount=from=uv,source=/usr/local/bin/uv,target=/bin/uv \
|
||||||
|
--mount=type=cache,target=/root/.cache/uv \
|
||||||
|
uv sync --locked --no-dev
|
||||||
|
|
||||||
|
CMD ["/usr/local/bin/python", "-m", "zangramru"]
|
||||||
|
|
||||||
|
# -----------------------------------
|
||||||
|
# STAGE 3: development build
|
||||||
|
# Includes dev dependencies
|
||||||
|
# -----------------------------------
|
||||||
|
FROM prod AS dev
|
||||||
|
|
||||||
|
RUN --mount=from=uv,source=/usr/local/bin/uv,target=/bin/uv \
|
||||||
|
--mount=type=cache,target=/root/.cache/uv \
|
||||||
|
uv sync --locked --all-groups
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
# zangramru
|
||||||
|
|
||||||
|
This project was generated using fastapi_template.
|
||||||
|
|
||||||
|
## UV
|
||||||
|
|
||||||
|
This project uses uv. It's a modern dependency management
|
||||||
|
tool.
|
||||||
|
|
||||||
|
To run the project use this set of commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv sync --locked
|
||||||
|
uv run -m zangramru
|
||||||
|
```
|
||||||
|
|
||||||
|
This will start the server on the configured host.
|
||||||
|
|
||||||
|
You can find swagger documentation at `/api/docs`.
|
||||||
|
|
||||||
|
You can read more about uv here: https://docs.astral.sh/ruff/
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
|
||||||
|
You can start the project with docker using this command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up --build
|
||||||
|
```
|
||||||
|
|
||||||
|
This command exposes the web application on port 8000, mounts current directory and enables autoreload.
|
||||||
|
|
||||||
|
But you have to rebuild image every time you modify `uv.lock` or `pyproject.toml` with this command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project structure
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ tree "zangramru"
|
||||||
|
zangramru
|
||||||
|
├── conftest.py # Fixtures for all tests.
|
||||||
|
├── db # module contains db configurations
|
||||||
|
│ ├── dao # Data Access Objects. Contains different classes to interact with database.
|
||||||
|
│ └── models # Package contains different models for ORMs.
|
||||||
|
├── __main__.py # Startup script. Starts uvicorn.
|
||||||
|
├── services # Package for different external services such as rabbit or redis etc.
|
||||||
|
├── settings.py # Main configuration settings for project.
|
||||||
|
├── static # Static content.
|
||||||
|
├── tests # Tests for project.
|
||||||
|
└── web # Package contains web server. Handlers, startup config.
|
||||||
|
├── api # Package with all handlers.
|
||||||
|
│ └── router.py # Main router.
|
||||||
|
├── application.py # FastAPI application configuration.
|
||||||
|
└── lifespan.py # Contains actions to perform on startup and shutdown.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
This application can be configured with environment variables.
|
||||||
|
|
||||||
|
You can create `.env` file in the root directory and place all
|
||||||
|
environment variables here.
|
||||||
|
|
||||||
|
All environment variables should start with "ZANGRAMRU_" prefix.
|
||||||
|
|
||||||
|
For example if you see in your "zangramru/settings.py" a variable named like
|
||||||
|
`random_parameter`, you should provide the "ZANGRAMRU_RANDOM_PARAMETER"
|
||||||
|
variable to configure the value. This behaviour can be changed by overriding `env_prefix` property
|
||||||
|
in `zangramru.settings.Settings.Config`.
|
||||||
|
|
||||||
|
An example of .env file:
|
||||||
|
```bash
|
||||||
|
ZANGRAMRU_RELOAD="True"
|
||||||
|
ZANGRAMRU_PORT="8000"
|
||||||
|
ZANGRAMRU_ENVIRONMENT="dev"
|
||||||
|
```
|
||||||
|
|
||||||
|
You can read more about BaseSettings class here: https://pydantic-docs.helpmanual.io/usage/settings/
|
||||||
|
|
||||||
|
## Pre-commit
|
||||||
|
|
||||||
|
To install pre-commit simply run inside the shell:
|
||||||
|
```bash
|
||||||
|
pre-commit install
|
||||||
|
```
|
||||||
|
|
||||||
|
pre-commit is very useful to check your code before publishing it.
|
||||||
|
It's configured using .pre-commit-config.yaml file.
|
||||||
|
|
||||||
|
By default it runs:
|
||||||
|
* mypy (validates types);
|
||||||
|
* ruff (spots possible bugs);
|
||||||
|
|
||||||
|
|
||||||
|
You can read more about pre-commit here: https://pre-commit.com/
|
||||||
|
|
||||||
|
## Migrations
|
||||||
|
|
||||||
|
If you want to migrate your database, you should run following commands:
|
||||||
|
```bash
|
||||||
|
# You have to set a PICCOLO_CONF variable
|
||||||
|
export PICCOLO_CONF="zangramru.piccolo_conf"
|
||||||
|
# Now you can easily run migrations using
|
||||||
|
piccolo migrations forwards all
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reverting migrations
|
||||||
|
|
||||||
|
If you want to revert migrations, you should run:
|
||||||
|
```bash
|
||||||
|
```
|
||||||
|
|
||||||
|
### Migration generation
|
||||||
|
|
||||||
|
To generate migrations you should run:
|
||||||
|
```bash
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Running tests
|
||||||
|
|
||||||
|
If you want to run it in docker, simply run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose run --build --rm api pytest -vv .
|
||||||
|
docker compose down
|
||||||
|
```
|
||||||
|
|
||||||
|
For running tests on your local machine.
|
||||||
|
1. you need to start all aux services.
|
||||||
|
|
||||||
|
We can do so by using our `docker-compose.yaml` configuration. It already has everything we need.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d --wait db
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Run tests.
|
||||||
|
```bash
|
||||||
|
pytest -vv .
|
||||||
|
```
|
||||||
|
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
services:
|
||||||
|
api: &main_app
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
target: dev
|
||||||
|
dockerfile: ./Dockerfile
|
||||||
|
image: zangramru:${ZANGRAMRU_VERSION:-latest}
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
# Exposes application port.
|
||||||
|
- "8000:8000"
|
||||||
|
env_file:
|
||||||
|
- path: .env
|
||||||
|
required: false
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
migrator:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
environment:
|
||||||
|
ZANGRAMRU_HOST: 0.0.0.0
|
||||||
|
ZANGRAMRU_RELOAD: "True"
|
||||||
|
ZANGRAMRU_DB_HOST: zangramru-db
|
||||||
|
ZANGRAMRU_DB_PORT: 5432
|
||||||
|
ZANGRAMRU_DB_USER: zangramru
|
||||||
|
ZANGRAMRU_DB_PASS: zangramru
|
||||||
|
ZANGRAMRU_DB_BASE: zangramru
|
||||||
|
volumes:
|
||||||
|
- .:/app/src/
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres:18.3-trixie
|
||||||
|
hostname: zangramru-db
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
environment:
|
||||||
|
POSTGRES_PASSWORD: "zangramru"
|
||||||
|
POSTGRES_USER: "zangramru"
|
||||||
|
POSTGRES_DB: "zangramru"
|
||||||
|
volumes:
|
||||||
|
- zangramru-db-data:/var/lib/postgresql
|
||||||
|
restart: always
|
||||||
|
healthcheck:
|
||||||
|
test: pg_isready -U zangramru
|
||||||
|
interval: 2s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 40
|
||||||
|
|
||||||
|
migrator:
|
||||||
|
<<: *main_app
|
||||||
|
restart: "no"
|
||||||
|
command: piccolo migrations forwards all
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
zangramru-db-data:
|
||||||
|
name: zangramru-db-data
|
||||||
+164
@@ -0,0 +1,164 @@
|
|||||||
|
[project]
|
||||||
|
name = "zangramru"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12,<4.0"
|
||||||
|
description = ""
|
||||||
|
authors = []
|
||||||
|
maintainers = []
|
||||||
|
readme = "README.md"
|
||||||
|
dependencies = [
|
||||||
|
"fastapi >=0.122.0,<1",
|
||||||
|
"uvicorn[standard] >=0.38.0,<1",
|
||||||
|
"pydantic >=2.12.5,<3",
|
||||||
|
"pydantic-settings >=2.12.0,<3",
|
||||||
|
"yarl >=1.22.0,<2",
|
||||||
|
"ujson >=5.11.0,<6",
|
||||||
|
"piccolo[postgres] >=1.30.0,<2",
|
||||||
|
"httptools >=0.7.1,<1",
|
||||||
|
"pymongo >=4.15.4,<5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
dev = [
|
||||||
|
"pytest >=9.0.1,<10",
|
||||||
|
"ruff >=0.14.6,<1",
|
||||||
|
"mypy >=1.19.0,<2",
|
||||||
|
"pre-commit >=4.5.0,<5",
|
||||||
|
"pytest-cov >=7.0.0,<8",
|
||||||
|
"anyio >=4.11.0,<5",
|
||||||
|
"pytest-env >=1.2.0,<2",
|
||||||
|
"httpx >=0.28.1,<1",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
[tool.uv]
|
||||||
|
default-groups = ["dev"]
|
||||||
|
|
||||||
|
[tool.isort]
|
||||||
|
profile = "black"
|
||||||
|
multi_line_output = 3
|
||||||
|
src_paths = ["zangramru",]
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
python_version = "3.13"
|
||||||
|
strict = true
|
||||||
|
ignore_missing_imports = true
|
||||||
|
allow_subclassing_any = true
|
||||||
|
allow_untyped_calls = true
|
||||||
|
pretty = true
|
||||||
|
show_error_codes = true
|
||||||
|
implicit_reexport = true
|
||||||
|
allow_untyped_decorators = true
|
||||||
|
warn_unused_ignores = false
|
||||||
|
warn_return_any = false
|
||||||
|
namespace_packages = true
|
||||||
|
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
addopts = "-p no:unraisableexception"
|
||||||
|
filterwarnings = [
|
||||||
|
"error",
|
||||||
|
"ignore::DeprecationWarning",
|
||||||
|
"ignore:.*unclosed.*:ResourceWarning",
|
||||||
|
]
|
||||||
|
anyio_mode = "auto"
|
||||||
|
env = [
|
||||||
|
"ZANGRAMRU_ENVIRONMENT=pytest",
|
||||||
|
"ZANGRAMRU_DB_BASE=zangramru_test",
|
||||||
|
"PICCOLO_CONF=zangramru.piccolo_conf",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
target-version = "py313"
|
||||||
|
# List of enabled rulsets.
|
||||||
|
# See https://docs.astral.sh/ruff/rules/ for more information.
|
||||||
|
lint.select = [ "E", # Error
|
||||||
|
"F", # Pyflakes
|
||||||
|
"W", # Pycodestyle
|
||||||
|
"C90", # McCabe complexity
|
||||||
|
"I", # Isort
|
||||||
|
"N", # pep8-naming
|
||||||
|
"D", # Pydocstyle
|
||||||
|
"ANN", # Pytype annotations
|
||||||
|
"S", # Bandit
|
||||||
|
"B", # Bugbear
|
||||||
|
"C4", # Comprehensions
|
||||||
|
"ISC", # Implicit string concat
|
||||||
|
"PIE", # Unnecessary code
|
||||||
|
"T20", # Catch prints
|
||||||
|
"PYI", # validate pyi files
|
||||||
|
"Q", # Checks for quotes
|
||||||
|
"RSE", # Checks raise statements
|
||||||
|
"RET", # Checks return statements
|
||||||
|
"SLF", # Self checks
|
||||||
|
"SIM", # Simplificator
|
||||||
|
"PTH", # Pathlib checks
|
||||||
|
"ERA", # Checks for commented out code
|
||||||
|
"PL", # PyLint checks
|
||||||
|
"RUF", # Specific to Ruff checks
|
||||||
|
"FA102", # Future annotations
|
||||||
|
"UP", # Python updates
|
||||||
|
]
|
||||||
|
lint.ignore = [
|
||||||
|
"D105", # Missing docstring in magic method
|
||||||
|
"D107", # Missing docstring in __init__
|
||||||
|
"B008", # Do not perform function calls in argument defaults
|
||||||
|
"D211", # No blank lines allowed before class docstring
|
||||||
|
"D212", # Multi-line docstring summary should start at the first line
|
||||||
|
"D401", # First line should be in imperative mood
|
||||||
|
"D104", # Missing docstring in public package
|
||||||
|
"D100", # Missing docstring in public module
|
||||||
|
"D202", # No blank lines allowed after function docstring
|
||||||
|
"ANN401", # typing.Any are disallowed in `**kwargs
|
||||||
|
"PLR0913", # Too many arguments for function call
|
||||||
|
"D106", # Missing docstring in public nested class
|
||||||
|
"UP043", # Unnecessary default argument specified (conflicts with mypy).
|
||||||
|
]
|
||||||
|
exclude = [
|
||||||
|
"zangramru/db/migrations",
|
||||||
|
".venv/"
|
||||||
|
]
|
||||||
|
lint.mccabe = { max-complexity = 10 }
|
||||||
|
line-length = 88
|
||||||
|
|
||||||
|
[tool.ruff.lint.per-file-ignores]
|
||||||
|
"tests/*" = [
|
||||||
|
"S101", # Use of assert detected
|
||||||
|
"PLC0415", # Import outside toplevel
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.ruff.lint.pydocstyle]
|
||||||
|
convention = "pep257"
|
||||||
|
ignore-decorators = ["typing.overload"]
|
||||||
|
|
||||||
|
[tool.ruff.lint.pylint]
|
||||||
|
allow-magic-value-types = ["int", "str", "float", "bytes"]
|
||||||
|
|
||||||
|
[tool.ruff.lint.flake8-bugbear]
|
||||||
|
extend-immutable-calls = [
|
||||||
|
"fastapi.Depends",
|
||||||
|
"fastapi.Query",
|
||||||
|
]
|
||||||
|
|
||||||
|
[fastapi-template.options]
|
||||||
|
project_name = "zangramru"
|
||||||
|
api_type = "rest"
|
||||||
|
enable_redis = "None"
|
||||||
|
enable_rmq = "None"
|
||||||
|
ci_type = "github"
|
||||||
|
enable_migrations = "True"
|
||||||
|
enable_taskiq = "None"
|
||||||
|
enable_routers = "True"
|
||||||
|
enable_kafka = "None"
|
||||||
|
enable_nats = "None"
|
||||||
|
enable_loguru = "None"
|
||||||
|
traefik_labels = "None"
|
||||||
|
add_dummy = "None"
|
||||||
|
orm = "piccolo"
|
||||||
|
self_hosted_swagger = "None"
|
||||||
|
prometheus_enabled = "None"
|
||||||
|
sentry_enabled = "None"
|
||||||
|
otlp_enabled = "None"
|
||||||
|
gunicorn = "None"
|
||||||
|
add_users = "None"
|
||||||
|
cookie_auth = "None"
|
||||||
|
jwt_auth = "None"
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
"""Tests for zangramru."""
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
from collections.abc import AsyncGenerator
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from httpx import ASGITransport, AsyncClient
|
||||||
|
from piccolo.conf.apps import Finder
|
||||||
|
from piccolo.engine.postgres import PostgresEngine
|
||||||
|
from piccolo.table import create_tables, drop_tables
|
||||||
|
|
||||||
|
from zangramru.settings import settings
|
||||||
|
from zangramru.web.application import get_app
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def anyio_backend() -> str:
|
||||||
|
"""
|
||||||
|
Backend for anyio pytest plugin.
|
||||||
|
|
||||||
|
:return: backend name.
|
||||||
|
"""
|
||||||
|
return "asyncio"
|
||||||
|
|
||||||
|
|
||||||
|
async def drop_database(engine: PostgresEngine) -> None:
|
||||||
|
"""
|
||||||
|
Drops test database.
|
||||||
|
|
||||||
|
:param engine: engine connected to postgres database.
|
||||||
|
"""
|
||||||
|
await engine.run_ddl(
|
||||||
|
"SELECT pg_terminate_backend(pg_stat_activity.pid) " # noqa: S608
|
||||||
|
"FROM pg_stat_activity "
|
||||||
|
f"WHERE pg_stat_activity.datname = '{settings.db_base}' "
|
||||||
|
"AND pid <> pg_backend_pid();",
|
||||||
|
)
|
||||||
|
await engine.run_ddl(
|
||||||
|
f"DROP DATABASE {settings.db_base};",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
async def setup_db() -> AsyncGenerator[None, None]:
|
||||||
|
"""
|
||||||
|
Fixture to create all tables before test and drop them after.
|
||||||
|
|
||||||
|
:yield: nothing.
|
||||||
|
"""
|
||||||
|
engine = PostgresEngine(
|
||||||
|
config={
|
||||||
|
"database": "postgres",
|
||||||
|
"user": settings.db_user,
|
||||||
|
"password": settings.db_pass,
|
||||||
|
"host": settings.db_host,
|
||||||
|
"port": settings.db_port,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await engine.start_connection_pool()
|
||||||
|
|
||||||
|
db_exists = await engine.run_ddl(
|
||||||
|
f"SELECT 1 FROM pg_database WHERE datname='{settings.db_base}'" # noqa: S608
|
||||||
|
)
|
||||||
|
if db_exists:
|
||||||
|
await drop_database(engine)
|
||||||
|
await engine.run_ddl(f"CREATE DATABASE {settings.db_base}")
|
||||||
|
tables = Finder().get_table_classes()
|
||||||
|
create_tables(*tables, if_not_exists=True)
|
||||||
|
|
||||||
|
yield
|
||||||
|
|
||||||
|
drop_tables(*tables)
|
||||||
|
await drop_database(engine)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def fastapi_app() -> FastAPI:
|
||||||
|
"""
|
||||||
|
Fixture for creating FastAPI app.
|
||||||
|
|
||||||
|
:return: fastapi app with mocked dependencies.
|
||||||
|
"""
|
||||||
|
application = get_app()
|
||||||
|
return application # noqa: RET504
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
async def client(
|
||||||
|
fastapi_app: FastAPI, anyio_backend: Any
|
||||||
|
) -> AsyncGenerator[AsyncClient, None]:
|
||||||
|
"""
|
||||||
|
Fixture that creates client for requesting server.
|
||||||
|
|
||||||
|
:param fastapi_app: the application.
|
||||||
|
:yield: client for the app.
|
||||||
|
"""
|
||||||
|
async with AsyncClient(
|
||||||
|
transport=ASGITransport(fastapi_app), base_url="http://test", timeout=2.0
|
||||||
|
) as ac:
|
||||||
|
yield ac
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import uuid
|
||||||
|
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from httpx import AsyncClient
|
||||||
|
from starlette import status
|
||||||
|
|
||||||
|
|
||||||
|
async def test_echo(fastapi_app: FastAPI, client: AsyncClient) -> None:
|
||||||
|
"""
|
||||||
|
Tests that echo route works.
|
||||||
|
|
||||||
|
:param fastapi_app: current application.
|
||||||
|
:param client: client for the app.
|
||||||
|
"""
|
||||||
|
url = fastapi_app.url_path_for("send_echo_message")
|
||||||
|
message = uuid.uuid4().hex
|
||||||
|
response = await client.post(url, json={"message": message})
|
||||||
|
assert response.status_code == status.HTTP_200_OK
|
||||||
|
assert response.json()["message"] == message
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
from fastapi import FastAPI
|
||||||
|
from httpx import AsyncClient
|
||||||
|
from starlette import status
|
||||||
|
|
||||||
|
|
||||||
|
async def test_health(client: AsyncClient, fastapi_app: FastAPI) -> None:
|
||||||
|
"""
|
||||||
|
Checks the health endpoint.
|
||||||
|
|
||||||
|
:param client: client for the app.
|
||||||
|
:param fastapi_app: current FastAPI application.
|
||||||
|
"""
|
||||||
|
url = fastapi_app.url_path_for("health_check")
|
||||||
|
response = await client.get(url)
|
||||||
|
assert response.status_code == status.HTTP_200_OK
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
"""zangramru package."""
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
import uvicorn
|
||||||
|
|
||||||
|
from zangramru.settings import settings
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
"""Entrypoint of the application."""
|
||||||
|
os.environ["PICCOLO_CONF"] = "zangramru.piccolo_conf"
|
||||||
|
uvicorn.run(
|
||||||
|
"zangramru.web.application:get_app",
|
||||||
|
workers=settings.workers_count,
|
||||||
|
host=settings.host,
|
||||||
|
port=settings.port,
|
||||||
|
reload=settings.reload,
|
||||||
|
log_level=settings.log_level.value.lower(),
|
||||||
|
access_log=True,
|
||||||
|
factory=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from piccolo.conf.apps import AppConfig, table_finder
|
||||||
|
|
||||||
|
CURRENT_DIRECTORY = Path(__file__).parent
|
||||||
|
|
||||||
|
|
||||||
|
APP_CONFIG = AppConfig(
|
||||||
|
app_name="zangramru_db",
|
||||||
|
migrations_folder_path=str(CURRENT_DIRECTORY / "migrations"),
|
||||||
|
table_classes=table_finder(modules=[]),
|
||||||
|
migration_dependencies=[],
|
||||||
|
commands=[],
|
||||||
|
)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
"""zangramru models."""
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
from zangramru.settings import settings
|
||||||
|
|
||||||
|
|
||||||
|
class InterceptHandler(logging.Handler):
|
||||||
|
"""
|
||||||
|
Default handler from examples in loguru documentation.
|
||||||
|
|
||||||
|
This handler intercepts all log requests and
|
||||||
|
passes them to loguru.
|
||||||
|
|
||||||
|
For more info see:
|
||||||
|
https://loguru.readthedocs.io/en/stable/overview.html#entirely-compatible-with-standard-logging
|
||||||
|
"""
|
||||||
|
|
||||||
|
def emit(self, record: logging.LogRecord) -> None: # pragma: no cover
|
||||||
|
"""
|
||||||
|
Propagates logs to loguru.
|
||||||
|
|
||||||
|
:param record: record to log.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
level: str | int = logger.level(record.levelname).name
|
||||||
|
except ValueError:
|
||||||
|
level = record.levelno
|
||||||
|
|
||||||
|
# Find caller from where originated the logged message
|
||||||
|
frame, depth = logging.currentframe(), 2
|
||||||
|
while frame.f_code.co_filename == logging.__file__:
|
||||||
|
frame = frame.f_back # type: ignore
|
||||||
|
depth += 1
|
||||||
|
|
||||||
|
logger.opt(depth=depth, exception=record.exc_info).log(
|
||||||
|
level,
|
||||||
|
record.getMessage(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def configure_logging() -> None: # pragma: no cover
|
||||||
|
"""Configures logging."""
|
||||||
|
intercept_handler = InterceptHandler()
|
||||||
|
|
||||||
|
logging.basicConfig(handlers=[intercept_handler], level=logging.NOTSET)
|
||||||
|
|
||||||
|
for logger_name in logging.root.manager.loggerDict:
|
||||||
|
if logger_name.startswith("uvicorn."):
|
||||||
|
logging.getLogger(logger_name).handlers = []
|
||||||
|
|
||||||
|
# change handler for default uvicorn logger
|
||||||
|
logging.getLogger("uvicorn").handlers = [intercept_handler]
|
||||||
|
logging.getLogger("uvicorn.access").handlers = [intercept_handler]
|
||||||
|
|
||||||
|
# set logs output, level and format
|
||||||
|
logger.remove()
|
||||||
|
logger.add(
|
||||||
|
sys.stdout,
|
||||||
|
level=settings.log_level.value,
|
||||||
|
)
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
from piccolo.conf.apps import AppRegistry
|
||||||
|
from piccolo.engine.postgres import PostgresEngine
|
||||||
|
|
||||||
|
from zangramru.settings import settings
|
||||||
|
|
||||||
|
DB = PostgresEngine(
|
||||||
|
config={
|
||||||
|
"database": settings.db_base,
|
||||||
|
"user": settings.db_user,
|
||||||
|
"password": settings.db_pass,
|
||||||
|
"host": settings.db_host,
|
||||||
|
"port": settings.db_port,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
APP_REGISTRY = AppRegistry(apps=["zangramru.db.app_conf"])
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
"""Services for zangramru."""
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import enum
|
||||||
|
from pathlib import Path
|
||||||
|
from tempfile import gettempdir
|
||||||
|
|
||||||
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
from yarl import URL
|
||||||
|
|
||||||
|
TEMP_DIR = Path(gettempdir())
|
||||||
|
|
||||||
|
|
||||||
|
class LogLevel(enum.StrEnum):
|
||||||
|
"""Possible log levels."""
|
||||||
|
|
||||||
|
NOTSET = "NOTSET"
|
||||||
|
DEBUG = "DEBUG"
|
||||||
|
INFO = "INFO"
|
||||||
|
WARNING = "WARNING"
|
||||||
|
ERROR = "ERROR"
|
||||||
|
FATAL = "FATAL"
|
||||||
|
|
||||||
|
|
||||||
|
class Settings(BaseSettings):
|
||||||
|
"""
|
||||||
|
Application settings.
|
||||||
|
|
||||||
|
These parameters can be configured
|
||||||
|
with environment variables.
|
||||||
|
"""
|
||||||
|
|
||||||
|
host: str = "127.0.0.1"
|
||||||
|
port: int = 8000
|
||||||
|
# quantity of workers for uvicorn
|
||||||
|
workers_count: int = 1
|
||||||
|
# Enable uvicorn reloading
|
||||||
|
reload: bool = False
|
||||||
|
|
||||||
|
# Current environment
|
||||||
|
environment: str = "dev"
|
||||||
|
|
||||||
|
log_level: LogLevel = LogLevel.INFO
|
||||||
|
# Variables for the database
|
||||||
|
db_host: str = "localhost"
|
||||||
|
db_port: int = 5432
|
||||||
|
db_user: str = "zangramru"
|
||||||
|
db_pass: str = "zangramru" # noqa: S105
|
||||||
|
db_base: str = "zangramru"
|
||||||
|
db_echo: bool = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def db_url(self) -> URL:
|
||||||
|
"""
|
||||||
|
Assemble database URL from settings.
|
||||||
|
|
||||||
|
:return: database URL.
|
||||||
|
"""
|
||||||
|
return URL.build(
|
||||||
|
scheme="postgresql",
|
||||||
|
host=self.db_host,
|
||||||
|
port=self.db_port,
|
||||||
|
user=self.db_user,
|
||||||
|
password=self.db_pass,
|
||||||
|
path=f"/{self.db_base}",
|
||||||
|
)
|
||||||
|
|
||||||
|
model_config = SettingsConfigDict(
|
||||||
|
env_file=".env",
|
||||||
|
env_prefix="ZANGRAMRU_",
|
||||||
|
env_file_encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
"""WEB API for zangramru."""
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
"""zangramru API package."""
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
"""Echo API."""
|
||||||
|
|
||||||
|
from .views import router
|
||||||
|
|
||||||
|
__all__ = ["router"]
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class Message(BaseModel):
|
||||||
|
"""Simple message model."""
|
||||||
|
|
||||||
|
message: str
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from zangramru.web.api.echo.schema import Message
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/", response_model=Message)
|
||||||
|
async def send_echo_message(
|
||||||
|
incoming_message: Message,
|
||||||
|
) -> Message:
|
||||||
|
"""
|
||||||
|
Sends echo back to user.
|
||||||
|
|
||||||
|
:param incoming_message: incoming message.
|
||||||
|
:returns: message same as the incoming.
|
||||||
|
"""
|
||||||
|
return incoming_message
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
"""API for checking project status."""
|
||||||
|
|
||||||
|
from zangramru.web.api.monitoring.views import router
|
||||||
|
|
||||||
|
__all__ = ["router"]
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/health")
|
||||||
|
def health_check() -> None:
|
||||||
|
"""
|
||||||
|
Checks the health of a project.
|
||||||
|
|
||||||
|
It returns 200 if the project is healthy.
|
||||||
|
"""
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
from fastapi.routing import APIRouter
|
||||||
|
|
||||||
|
from zangramru.web.api import echo, monitoring
|
||||||
|
|
||||||
|
api_router = APIRouter()
|
||||||
|
api_router.include_router(monitoring.router)
|
||||||
|
api_router.include_router(echo.router, prefix="/echo", tags=["echo"])
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
from zangramru.web.api.router import api_router
|
||||||
|
from zangramru.web.lifespan import lifespan_setup
|
||||||
|
|
||||||
|
|
||||||
|
def get_app() -> FastAPI:
|
||||||
|
"""
|
||||||
|
Get FastAPI application.
|
||||||
|
|
||||||
|
This is the main constructor of an application.
|
||||||
|
|
||||||
|
:return: application.
|
||||||
|
"""
|
||||||
|
app = FastAPI(
|
||||||
|
title="zangramru",
|
||||||
|
lifespan=lifespan_setup,
|
||||||
|
docs_url="/api/docs",
|
||||||
|
redoc_url="/api/redoc",
|
||||||
|
openapi_url="/api/openapi.json",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Main router for the API.
|
||||||
|
app.include_router(router=api_router, prefix="/api")
|
||||||
|
|
||||||
|
return app
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
from collections.abc import AsyncGenerator
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def lifespan_setup(
|
||||||
|
app: FastAPI,
|
||||||
|
) -> AsyncGenerator[None, None]: # pragma: no cover
|
||||||
|
"""
|
||||||
|
Actions to run on application startup.
|
||||||
|
|
||||||
|
This function uses fastAPI app to store data
|
||||||
|
in the state, such as db_engine.
|
||||||
|
|
||||||
|
:param app: the fastAPI application.
|
||||||
|
:return: function that actually performs actions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
app.middleware_stack = None
|
||||||
|
app.middleware_stack = app.build_middleware_stack()
|
||||||
|
|
||||||
|
yield
|
||||||
Reference in New Issue
Block a user