From 439c194d9f6c044b361b727168124c9f7ced5d8a Mon Sep 17 00:00:00 2001 From: Alex Pavlov Date: Wed, 20 Nov 2024 21:34:58 +0600 Subject: [PATCH 1/8] add test task celery --- api_v1/users/tasks.py | 11 +++++++++++ app_includes/logs_errors.py | 3 +++ config/celery/connection.py | 4 ++-- 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 api_v1/users/tasks.py diff --git a/api_v1/users/tasks.py b/api_v1/users/tasks.py new file mode 100644 index 0000000..1236c4d --- /dev/null +++ b/api_v1/users/tasks.py @@ -0,0 +1,11 @@ +from config import celery_app +import asyncio + + +@celery_app.task +async def time_sleep_task(): + """ + Тестовая задача для Celery + """ + asyncio.sleep(2.0) + return 'Task is done' diff --git a/app_includes/logs_errors.py b/app_includes/logs_errors.py index 7064531..c52b2fd 100644 --- a/app_includes/logs_errors.py +++ b/app_includes/logs_errors.py @@ -10,6 +10,9 @@ def register_errors(app: FastAPI) -> None: + """ + Крючек для логирования различных исключений + """ @app.exception_handler(ValidationError) async def validation_error_handler( request: Request, diff --git a/config/celery/connection.py b/config/celery/connection.py index ba68115..bd3a761 100644 --- a/config/celery/connection.py +++ b/config/celery/connection.py @@ -9,7 +9,7 @@ class Celery(celery.Celery): """ - Инициализация Celery + Инициализация асинхронного Celery """ def __init__(self, *args, **kwargs) -> None: @@ -41,4 +41,4 @@ def wrapper(*args, app = Celery(__name__) app.conf.broker_url = settings.rabbit.broker_url -app.autodiscover_tasks(packages=[]) +app.autodiscover_tasks(packages=['api_v1.users']) From 3dcd386d0a1ad97fbb5bc61fa08abfb423f9b7c6 Mon Sep 17 00:00:00 2001 From: Alex Pavlov Date: Wed, 20 Nov 2024 22:02:50 +0600 Subject: [PATCH 2/8] test readme --- README.md | 4 ++++ {docker/alertmanager => alertmanager}/config.yml | 0 docker-compose.yml | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) rename {docker/alertmanager => alertmanager}/config.yml (100%) diff --git a/README.md b/README.md index e69de29..1ec61d0 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,4 @@ +
+Some text +Some text +
\ No newline at end of file diff --git a/docker/alertmanager/config.yml b/alertmanager/config.yml similarity index 100% rename from docker/alertmanager/config.yml rename to alertmanager/config.yml diff --git a/docker-compose.yml b/docker-compose.yml index f1128e6..e7549fe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -105,7 +105,7 @@ services: image: prom/alertmanager:v0.20.0 container_name: alertmanager volumes: - - ./docker/alertmanager:/etc/alertmanager + - ./alertmanager:/etc/alertmanager command: - '--config.file=/etc/alertmanager/config.yml' - '--storage.path=/alertmanager' From e7a0a9b991c4abb4b20a133151c412b5acdc0665 Mon Sep 17 00:00:00 2001 From: Alex Pavlov Date: Wed, 20 Nov 2024 23:45:31 +0600 Subject: [PATCH 3/8] readme view --- README.md | 97 +++++++++++++++++++++++++++++++++++++++++++++-- api_v1/routers.py | 3 ++ 2 files changed, 96 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1ec61d0..8ae5fe4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,93 @@ -
-Some text -Some text -
\ No newline at end of file +# Title +Данный шаблон был разработан для одной цели - облегчения и повышения качества +выполненых тестовых заданий в рамках **FastAPI**. + +# Quick start +Для тех кто уже знаком с реализацией и всеми деталями - могут приступить к установке. +## Enviroments +Необходимо заполнить **.env.sample** и в последствии перемеиновать его в **.env** +```python +# .env.sample +POSTGRES_PASSWORD=password # Пароль от базы данных (Настройка) +DB_PASSWORD=password # Пароль от базы данных (Использование) +TEST_POSTGRES_PASSWORD=password # Пароль от тестовой базы данный (Настройка) +TEST_DB_PASSWORD=password # Пароль от тестовой базы данных (Использование) +``` +## Docker +Шаблон находится под системой управления и контеризации - **Docker**. +Если у вас нет Docker - вы можете установить его с официального сайта: [Docker](https://www.docker.com/get-started/) +- Вам необходимо сделать "Билд" +```bash +docker compose build +``` +- Вам необходимо запустить окружение +```bash +docker compose up +``` +- После успешного запуска приложение будет доступно по адрессу: http://localhost:8080 + +# View +Обзор и детали данного шаблона +## Найболее используемые +Найболее используемые конструкции с которыми приходится часто взаимодействовать. +- registration routers + - В каждом приложений необходимо инициализировать router + ```python + # api/users/views.py + from fastapi import APIRouter + + + router = APIRouter(prefix='/users', + tags=['Users'], + ) + ``` + - Затем зарегистрировать роутер + ```python + # api_v1/routers.py + from api_v1.users.views import router as users + + + # В этой функции нужно по порядку регистрировать routers + def register_routers(app: FastAPI) -> None: + app.include_router( + router=users, + prefix=settings.API_PREFIX, + ) + ``` + После регистрации данные маршруты будут доступны. +- registration logs + - Логи захватывают все исключения возникшие в системе + и с помошью дисперичизации распределяется по нужным **file.log** + ```python + # app_includes/logs_errors.py + from fastapi import FastAPI + from api_v1.exeptions import ValidationError + + + # В данной функции регистрируются все исключения для захватывания Логами + def register_errors(app: FastAPI) -> None: + @app.exception_handler(ValidationError) + async def validation_error_handler( + request: Request, + exc: ValidationError, + ): + logger.opt(exception=True).warning(exc) + response = dict( + status=False, + error_code=exc.status_code, + message=exc.detail, + ) + return JSONResponse(response) + ``` + - Если вы пишете пользовательское исключение например: + ```python + from starlette.exceptions import HTTPException + + + class ValidationError(HTTPException): + + pass + ``` + То вам нужно его зарегистрировать как было показанно выше, + иначе logs не смогут выявить данное исключение и данные будут потеряны. +- registration middlaware \ No newline at end of file diff --git a/api_v1/routers.py b/api_v1/routers.py index c4cc0c1..c017092 100644 --- a/api_v1/routers.py +++ b/api_v1/routers.py @@ -5,6 +5,9 @@ def register_routers(app: FastAPI) -> None: + """ + Функция по регистрации роутеров + """ app.include_router( router=users, prefix=settings.API_PREFIX, From 05cb2a7454bcc19b9239bf032b425781538e2ef4 Mon Sep 17 00:00:00 2001 From: Alex Pavlov Date: Wed, 20 Nov 2024 23:51:06 +0600 Subject: [PATCH 4/8] change view --- README.md | 106 +++++++++++++++++++++++++++--------------------------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 8ae5fe4..752319a 100644 --- a/README.md +++ b/README.md @@ -30,64 +30,66 @@ docker compose up Обзор и детали данного шаблона ## Найболее используемые Найболее используемые конструкции с которыми приходится часто взаимодействовать. -- registration routers - - В каждом приложений необходимо инициализировать router - ```python - # api/users/views.py - from fastapi import APIRouter +### Registration Routers +- В каждом приложений необходимо инициализировать router +```python +# api/users/views.py +from fastapi import APIRouter - router = APIRouter(prefix='/users', - tags=['Users'], - ) - ``` - - Затем зарегистрировать роутер - ```python - # api_v1/routers.py - from api_v1.users.views import router as users +router = APIRouter( + prefix='/users', + tags=['Users'], + ) +``` +- Затем зарегистрировать роутер +```python +# api_v1/routers.py +from api_v1.users.views import router as users +from config import settings - # В этой функции нужно по порядку регистрировать routers - def register_routers(app: FastAPI) -> None: - app.include_router( - router=users, - prefix=settings.API_PREFIX, - ) - ``` - После регистрации данные маршруты будут доступны. -- registration logs - - Логи захватывают все исключения возникшие в системе - и с помошью дисперичизации распределяется по нужным **file.log** - ```python - # app_includes/logs_errors.py - from fastapi import FastAPI - from api_v1.exeptions import ValidationError +# В этой функции нужно по порядку регистрировать routers +def register_routers(app: FastAPI) -> None: +app.include_router( + router=users, + prefix=settings.API_PREFIX, +) +``` +После регистрации данные маршруты будут доступны. +### Registration Logs +- Логи захватывают все исключения возникшие в системе +и с помошью дисперичизации распределяется по нужным **file.log** +```python +# app_includes/logs_errors.py +from fastapi import FastAPI +from api_v1.exeptions import ValidationError - # В данной функции регистрируются все исключения для захватывания Логами - def register_errors(app: FastAPI) -> None: - @app.exception_handler(ValidationError) - async def validation_error_handler( - request: Request, - exc: ValidationError, - ): - logger.opt(exception=True).warning(exc) - response = dict( - status=False, - error_code=exc.status_code, - message=exc.detail, - ) - return JSONResponse(response) - ``` - - Если вы пишете пользовательское исключение например: - ```python - from starlette.exceptions import HTTPException +# В данной функции регистрируются все исключения для захватывания Логами +def register_errors(app: FastAPI) -> None: + @app.exception_handler(ValidationError) + async def validation_error_handler( + request: Request, + exc: ValidationError, + ): + logger.opt(exception=True).warning(exc) + response = dict( + status=False, + error_code=exc.status_code, + message=exc.detail, + ) + return JSONResponse(response) +``` +- Если вы пишете пользовательское исключение например: +```python +from starlette.exceptions import HTTPException - class ValidationError(HTTPException): +class ValidationError(HTTPException): - pass - ``` - То вам нужно его зарегистрировать как было показанно выше, - иначе logs не смогут выявить данное исключение и данные будут потеряны. -- registration middlaware \ No newline at end of file +pass +``` +То вам нужно его зарегистрировать как было показанно выше, +иначе logs не смогут выявить данное исключение и данные будут утеряны. +### Registration Middlaware \ No newline at end of file From bdd95d43e17af61dd35afd6164d29eb1f7e26318 Mon Sep 17 00:00:00 2001 From: Alex Pavlov Date: Thu, 21 Nov 2024 00:02:44 +0600 Subject: [PATCH 5/8] readme celery --- README.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 752319a..6d8974f 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,8 @@ docker compose build docker compose up ``` - После успешного запуска приложение будет доступно по адрессу: http://localhost:8080 +- Grafana: http://localhost:3000 +- Flower: http://localhost:5555 # View Обзор и детали данного шаблона @@ -57,6 +59,7 @@ app.include_router( ) ``` После регистрации данные маршруты будут доступны. + ### Registration Logs - Логи захватывают все исключения возникшие в системе и с помошью дисперичизации распределяется по нужным **file.log** @@ -92,4 +95,53 @@ pass ``` То вам нужно его зарегистрировать как было показанно выше, иначе logs не смогут выявить данное исключение и данные будут утеряны. -### Registration Middlaware \ No newline at end of file + +### Registration Middlaware +- Для регистрации Middlaware вам нужно добавить его в функцию +```python +from fastapi.middleware.cors import CORSMiddleware +from fastapi import FastAPI + +from config import settings + + +# Данная функция регистрирует все middleware +def register_middlewares(app: FastAPI) -> None: + app.add_middleware( + CORSMiddleware, + allow_origins=[ + settings.CURRENT_ORIGIN, + ], + allow_credentials=True, + allow_methods=['*'], + allow_headers=['*'], + ) +``` +- При появлении новых middleware добавляйте их по порядку в эту функцию + +### Celery +- Для регистрации task вам нужно создать файл с именем **tasks.py** в вашем приложении: +```python +# api_v1/users/tasks.py +from config import celery_app +import asyncio + + +@celery_app.task +async def time_sleep_task(): + """ + Тестовая задача для Celery + """ + asyncio.sleep(2.0) + return 'Task is done' +``` +- Затем добавить этот файл в список пакетов Celery +```python +# confin.celery.connection.py + +app = Celery(__name__) +app.conf.broker_url = settings.rabbit.broker_url +# Регистрация до окружения где находится tasks.py +app.autodiscover_tasks(packages=['api_v1.users']) +``` +- После этих действий ваша task будет зарегистрирована \ No newline at end of file From b5ef1aba244cc101691e56db8f587b82b416cfae Mon Sep 17 00:00:00 2001 From: Alex Pavlov Date: Thu, 21 Nov 2024 00:19:50 +0600 Subject: [PATCH 6/8] test readme --- README.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6d8974f..d028929 100644 --- a/README.md +++ b/README.md @@ -144,4 +144,30 @@ app.conf.broker_url = settings.rabbit.broker_url # Регистрация до окружения где находится tasks.py app.autodiscover_tasks(packages=['api_v1.users']) ``` -- После этих действий ваша task будет зарегистрирована \ No newline at end of file +- После этих действий ваша task будет зарегистрирована + +### Test +- Для тестирования у вас есть тестовая база данных, а так же +уже инициализированный отдельный клиент. +Cпособ реализации в **api_v1/tests/conftest.py** +- Что бы написать тестовую функцию которой нужен доступ к API, +вам нужно использовать fixture - client. +> [!NOTE] +> Для асинхронных тестов используйте **pytest.mark.asyncio** + +```python +# api_v1.tests.test_users.py +import pytest + + +@pytest.mark.asyncio +async def test_get_user_error(client: AsyncClient): + response = await client.get( + '/users/get', + ) + assert response.status_code == 400 +``` +- Для запуска используйте команду +```bash +pytest +``` \ No newline at end of file From 54419e61d98c6fc26e2d71662250acf17a984655 Mon Sep 17 00:00:00 2001 From: Alex Pavlov Date: Thu, 21 Nov 2024 00:23:33 +0600 Subject: [PATCH 7/8] fix readme --- README.md | 4 ++-- api_v1/users/tasks.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d028929..527ce2e 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ async def time_sleep_task(): """ Тестовая задача для Celery """ - asyncio.sleep(2.0) + await asyncio.sleep(2.0) return 'Task is done' ``` - Затем добавить этот файл в список пакетов Celery @@ -153,7 +153,7 @@ Cпособ реализации в **api_v1/tests/conftest.py** - Что бы написать тестовую функцию которой нужен доступ к API, вам нужно использовать fixture - client. > [!NOTE] -> Для асинхронных тестов используйте **pytest.mark.asyncio** +> Для асинхронных тестов используйте **@pytest.mark.asyncio** ```python # api_v1.tests.test_users.py diff --git a/api_v1/users/tasks.py b/api_v1/users/tasks.py index 1236c4d..2762e16 100644 --- a/api_v1/users/tasks.py +++ b/api_v1/users/tasks.py @@ -7,5 +7,5 @@ async def time_sleep_task(): """ Тестовая задача для Celery """ - asyncio.sleep(2.0) + await asyncio.sleep(2.0) return 'Task is done' From 93d43f111a578b3374af008150917276267983af Mon Sep 17 00:00:00 2001 From: Alex Pavlov Date: Thu, 21 Nov 2024 00:27:51 +0600 Subject: [PATCH 8/8] fix readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 527ce2e..a91d49e 100644 --- a/README.md +++ b/README.md @@ -53,10 +53,10 @@ from config import settings # В этой функции нужно по порядку регистрировать routers def register_routers(app: FastAPI) -> None: -app.include_router( - router=users, - prefix=settings.API_PREFIX, -) + app.include_router( + router=users, + prefix=settings.API_PREFIX, + ) ``` После регистрации данные маршруты будут доступны.