flow dump openapi.json

develop
Сергей Ванюшкин 2024-02-06 22:07:25 +03:00
parent 7d4c4d9be3
commit f8cca4b861
13 changed files with 255 additions and 36 deletions

View File

@ -1,11 +1,53 @@
import json
from fastapi import FastAPI from fastapi import FastAPI
from fastfood.routers.dish import router as dish_router from fastfood.routers.dish import router as dish_router
from fastfood.routers.menu import router as menu_router from fastfood.routers.menu import router as menu_router
from fastfood.routers.submenu import router as submenu_router from fastfood.routers.submenu import router as submenu_router
description = """
# 🔥🔥🔥Fastfood-API поможет тебе подкрепиться 🔥🔥🔥
### У нас есть Menu. Ты можеш выбрать блюда из кухни, которая тебе нравится
## Menu
Ты можешь **add menu**.
Ты можешь **read menu**.
Ты можешь **patch menu**.
Ты можешь **delete menu**.
### У нас есть в SubMenu, где ты сможешь найти
десерты/напитки/супчики/прочие вкусности
# SubMenu
Ты можешь **add submenu into menu**.
Ты можешь **read submenu**.
Ты можешь **patch submenu**.
Ты можешь **delete menu**.
### У нас есть в Dish, где ты сможешь найти блюдо по вкусу
# Dish
Ты можешь **add dish into submenu**.
Ты можешь **read dish**.
Ты можешь **patch dish**.
Ты можешь **delete dish**.
## Приятного аппетита
"""
tags_metadata = [ tags_metadata = [
{ {
'name': 'menu', 'name': 'menu',
@ -23,13 +65,10 @@ def create_app(redis=None) -> FastAPI:
""" """
Фабрика FastAPI. Фабрика FastAPI.
""" """
with open('openapi.json') as f:
js = json.load(f)
app = FastAPI( app = FastAPI(
title=js['info']['title'], title='Fastfood-API',
description=js['info']['description'], description=description,
version=js['info']['version'], version='0.0.3',
contact={ contact={
'name': 'Sergey Vanyushkin', 'name': 'Sergey Vanyushkin',
'url': 'http://pi3c.ru', 'url': 'http://pi3c.ru',

View File

@ -49,13 +49,13 @@ class DishRepository:
submenu_id: UUID, submenu_id: UUID,
dish_id: UUID, dish_id: UUID,
dish_data: Dish_db, dish_data: Dish_db,
) -> Dish: ) -> Dish | None:
query = update(Dish).where(Dish.id == dish_id).values(**dish_data.model_dump()) query = update(Dish).where(Dish.id == dish_id).values(**dish_data.model_dump())
await self.db.execute(query) await self.db.execute(query)
await self.db.commit() await self.db.commit()
qr = select(Dish).where(Dish.id == dish_id) qr = select(Dish).where(Dish.id == dish_id)
updated_submenu = await self.db.execute(qr) updated_submenu = await self.db.execute(qr)
return updated_submenu.scalars().one() return updated_submenu.scalar_one_or_none()
async def delete_dish_item( async def delete_dish_item(
self, self,

View File

@ -50,13 +50,13 @@ class MenuRepository:
self, self,
menu_id: UUID, menu_id: UUID,
menu: schemas.MenuBase, menu: schemas.MenuBase,
) -> Menu: ) -> Menu | None:
query = update(Menu).where(Menu.id == menu_id).values(**menu.model_dump()) query = update(Menu).where(Menu.id == menu_id).values(**menu.model_dump())
await self.db.execute(query) await self.db.execute(query)
await self.db.commit() await self.db.commit()
qr = select(Menu).where(Menu.id == menu_id) qr = select(Menu).where(Menu.id == menu_id)
updated_menu = await self.db.execute(qr) updated_menu = await self.db.execute(qr)
return updated_menu.scalar_one() return updated_menu.scalar_one_or_none()
async def delete_menu_item(self, menu_id: UUID) -> None: async def delete_menu_item(self, menu_id: UUID) -> None:
query = delete(Menu).where(Menu.id == menu_id) query = delete(Menu).where(Menu.id == menu_id)

View File

@ -31,7 +31,7 @@ class RedisRepository:
pool: redis.Redis = Depends(get_redis_pool), pool: redis.Redis = Depends(get_redis_pool),
) -> None: ) -> None:
self.pool = pool self.pool = pool
self.ttl = 2 self.ttl = 1800
async def get(self, key: str) -> Any | None: async def get(self, key: str) -> Any | None:
data = await self.pool.get(key) data = await self.pool.get(key)

View File

@ -59,7 +59,7 @@ class SubMenuRepository:
menu_id: UUID, menu_id: UUID,
submenu_id: UUID, submenu_id: UUID,
submenu_data: MenuBase, submenu_data: MenuBase,
) -> SubMenu: ) -> SubMenu | None:
query = ( query = (
update(SubMenu) update(SubMenu)
.where(SubMenu.id == submenu_id) .where(SubMenu.id == submenu_id)
@ -69,7 +69,7 @@ class SubMenuRepository:
await self.db.commit() await self.db.commit()
qr = select(SubMenu).where(SubMenu.id == submenu_id) qr = select(SubMenu).where(SubMenu.id == submenu_id)
updated_submenu = await self.db.execute(qr) updated_submenu = await self.db.execute(qr)
return updated_submenu.scalar_one() return updated_submenu.scalar_one_or_none()
async def delete_submenu_item(self, menu_id: UUID, submenu_id: UUID) -> None: async def delete_submenu_item(self, menu_id: UUID, submenu_id: UUID) -> None:
query = delete(SubMenu).where( query = delete(SubMenu).where(

View File

@ -11,7 +11,13 @@ router = APIRouter(
) )
@router.get('/', response_model=list[Dish]) @router.get(
'/',
response_model=list[Dish],
summary='Получить список блюд',
description='Этот метод позволяет получить список всех блюда по UUID'
' родительских меню и подменю',
)
async def get_dishes( async def get_dishes(
menu_id: UUID, menu_id: UUID,
submenu_id: UUID, submenu_id: UUID,
@ -22,7 +28,14 @@ async def get_dishes(
return result return result
@router.post('/', status_code=201, response_model=Dish) @router.post(
'/',
status_code=201,
response_model=Dish,
summary='Создать блюдо',
description='Этот метод позволяет создать блюдо по UUID'
'его родительских меню и подменю',
)
async def create_dish( async def create_dish(
menu_id: UUID, menu_id: UUID,
submenu_id: UUID, submenu_id: UUID,
@ -37,7 +50,19 @@ async def create_dish(
) )
@router.get('/{dish_id}', response_model=Dish) @router.get(
'/{dish_id}',
response_model=Dish,
summary='Получить блюдо',
description='Этот метод позволяет получить блюдо по его UUID'
' и UUID его родительских меню',
responses={
404: {
'description': 'Dish not found',
'content': {'application/json': {'example': {'detail': 'string'}}},
},
},
)
async def get_dish( async def get_dish(
menu_id: UUID, menu_id: UUID,
submenu_id: UUID, submenu_id: UUID,
@ -51,11 +76,26 @@ async def get_dish(
dish_id, dish_id,
) )
if not result: if not result:
raise HTTPException(status_code=404, detail='dish not found') raise HTTPException(
status_code=404,
detail=f'Блюдо c UUID={dish_id} не существует, доступ невозможен',
)
return result return result
@router.patch('/{dish_id}', response_model=Dish) @router.patch(
'/{dish_id}',
response_model=Dish,
summary='Обновить блюдо',
description='Этот метод позволяет обновить блюдо по его UUID'
' и UUID родительских меню',
responses={
404: {
'description': 'Dish not found',
'content': {'application/json': {'example': {'detail': 'string'}}},
},
},
)
async def update_dish( async def update_dish(
menu_id: UUID, menu_id: UUID,
submenu_id: UUID, submenu_id: UUID,
@ -70,10 +110,20 @@ async def update_dish(
dish_id, dish_id,
dish_data, dish_data,
) )
if not result:
raise HTTPException(
status_code=404,
detail=f'Блюдо c UUID={dish_id} не существует, обновление невозможно',
)
return result return result
@router.delete('/{dish_id}') @router.delete(
'/{dish_id}',
summary='Удалить блюдо',
description='Этот метод позволяет удалить блюдо по его UUID'
' и UUID родительских меню',
)
async def delete_dish( async def delete_dish(
menu_id: UUID, menu_id: UUID,
submenu_id: UUID, submenu_id: UUID,

View File

@ -11,7 +11,13 @@ router = APIRouter(
) )
@router.get('/', response_model=list[MenuRead]) @router.get(
'/',
status_code=200,
response_model=list[MenuRead],
summary='Получить список меню',
description='Этот метод позволяет получить все меню.',
)
async def get_menus( async def get_menus(
menu: MenuService = Depends(), menu: MenuService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(), background_tasks: BackgroundTasks = BackgroundTasks(),
@ -19,7 +25,13 @@ async def get_menus(
return await menu.read_menus() return await menu.read_menus()
@router.post('/', status_code=201, response_model=MenuRead) @router.post(
'/',
status_code=201,
response_model=MenuRead,
summary='Создать меню',
description='Этот метод позволяет создать меню',
)
async def add_menu( async def add_menu(
menu: MenuBase, menu: MenuBase,
responce: MenuService = Depends(), responce: MenuService = Depends(),
@ -28,7 +40,18 @@ async def add_menu(
return await responce.create_menu(menu) return await responce.create_menu(menu)
@router.get('/{menu_id}', response_model=MenuRead) @router.get(
'/{menu_id}',
response_model=MenuRead,
summary='Получить меню',
description='Этот метод позволяет получить меню по его UUID',
responses={
404: {
'description': 'Menu not found',
'content': {'application/json': {'example': {'detail': 'sting'}}},
},
},
)
async def get_menu( async def get_menu(
menu_id: UUID, menu_id: UUID,
responce: MenuService = Depends(), responce: MenuService = Depends(),
@ -37,11 +60,25 @@ async def get_menu(
result = await responce.read_menu(menu_id=menu_id) result = await responce.read_menu(menu_id=menu_id)
if not result: if not result:
raise HTTPException(status_code=404, detail='menu not found') raise HTTPException(
status_code=404,
detail=f'Меню c UUID={menu_id} не существует, доступ невозможен',
)
return result return result
@router.patch('/{menu_id}', response_model=MenuRead) @router.patch(
'/{menu_id}',
response_model=MenuRead,
summary='Обновить меню',
description='Этот метод позволяет изменить меню по его UUID',
responses={
404: {
'description': 'Menu not found',
'content': {'application/json': {'example': {'detail': 'string'}}},
},
},
)
async def update_menu( async def update_menu(
menu_id: UUID, menu_id: UUID,
menu: MenuBase, menu: MenuBase,
@ -52,10 +89,21 @@ async def update_menu(
menu_id=menu_id, menu_id=menu_id,
menu_data=menu, menu_data=menu,
) )
if not result:
raise HTTPException(
status_code=404,
detail=f'Меню c UUID={menu_id} не существует, Обновление невозможно',
)
return result return result
@router.delete('/{menu_id}') @router.delete(
'/{menu_id}',
status_code=200,
summary='Удалить меню',
description='Этот метод позволяет удалить меню по его UUID',
)
async def delete_menu( async def delete_menu(
menu_id: UUID, menu_id: UUID,
menu: MenuService = Depends(), menu: MenuService = Depends(),

View File

@ -11,7 +11,13 @@ router = APIRouter(
) )
@router.get('/', response_model=list[SubMenuRead]) @router.get(
'/',
response_model=list[SubMenuRead],
summary='Получить список подменю',
description='Этот метод позволяет получить список подменю основного меню'
' по UUID меню',
)
async def get_submenus( async def get_submenus(
menu_id: UUID, menu_id: UUID,
submenu: SubmenuService = Depends(), submenu: SubmenuService = Depends(),
@ -21,7 +27,13 @@ async def get_submenus(
return result return result
@router.post('/', status_code=201, response_model=SubMenuRead) @router.post(
'/',
status_code=201,
response_model=SubMenuRead,
summary='Создать подменю',
description='Этот метод позволяет создать подменю по UUID родителского меню',
)
async def create_submenu_item( async def create_submenu_item(
menu_id: UUID, menu_id: UUID,
submenu_data: MenuBase, submenu_data: MenuBase,
@ -35,7 +47,19 @@ async def create_submenu_item(
return result return result
@router.get('/{submenu_id}', response_model=SubMenuRead) @router.get(
'/{submenu_id}',
response_model=SubMenuRead,
summary='Получить подменю',
description='Этот метод позволяет получить подменю по его UUID'
' и UUID родительского меню',
responses={
404: {
'description': 'Submenu not found',
'content': {'application/json': {'example': {'detail': 'string'}}},
},
},
)
async def get_submenu( async def get_submenu(
menu_id: UUID, menu_id: UUID,
submenu_id: UUID, submenu_id: UUID,
@ -47,13 +71,25 @@ async def get_submenu(
submenu_id=submenu_id, submenu_id=submenu_id,
) )
if not result: if not result:
raise HTTPException(status_code=404, detail='submenu not found') raise HTTPException(
status_code=404,
detail=f'Подменю c UUID={submenu_id} не существует, доступ невозможен',
)
return result return result
@router.patch( @router.patch(
'/{submenu_id}', '/{submenu_id}',
response_model=SubMenuRead, response_model=SubMenuRead,
summary='Обновить подменю',
description='Этот метод позволяет обновить подменю по его UUID'
' и UUID родительского меню',
responses={
404: {
'description': 'Submenu not found',
'content': {'application/json': {'example': {'detail': 'string'}}},
},
},
) )
async def update_submenu( async def update_submenu(
menu_id: UUID, menu_id: UUID,
@ -67,10 +103,20 @@ async def update_submenu(
submenu_id=submenu_id, submenu_id=submenu_id,
submenu_data=submenu_data, submenu_data=submenu_data,
) )
if not result:
raise HTTPException(
status_code=404,
detail=f'Gjlvеню c UUID={submenu_id} не существует, обновление невозможно',
)
return result return result
@router.delete('/{submenu_id}') @router.delete(
'/{submenu_id}',
summary='Удалить подменю',
description='Этот метод позволяет удалить подменю по его UUID',
)
async def delete_submenu( async def delete_submenu(
menu_id: UUID, menu_id: UUID,
submenu_id: UUID, submenu_id: UUID,

View File

@ -103,14 +103,19 @@ class DishService:
async def update_dish( async def update_dish(
self, menu_id: UUID, submenu_id: UUID, dish_id, dish_data: DishBase self, menu_id: UUID, submenu_id: UUID, dish_id, dish_data: DishBase
) -> Dish: ) -> Dish | None:
dish_db = Dish_db(**dish_data.model_dump()) dish_db = Dish_db(**dish_data.model_dump())
data = await self.dish_repo.update_dish_item( data = await self.dish_repo.update_dish_item(
menu_id, submenu_id, dish_id, dish_db menu_id, submenu_id, dish_id, dish_db
) )
if data is None:
return None
dish = data.__dict__ dish = data.__dict__
dish['price'] = str(dish['price']) dish['price'] = str(dish['price'])
dish = Dish(**dish) dish = Dish(**dish)
await self.cache.set( await self.cache.set(
self.key( self.key(
'dish', 'dish',

View File

@ -87,8 +87,10 @@ class MenuService:
) )
return menu return menu
async def update_menu(self, menu_id: UUID, menu_data) -> MenuRead: async def update_menu(self, menu_id: UUID, menu_data) -> MenuRead | None:
data = await self.menu_repo.update_menu_item(menu_id, menu_data) data = await self.menu_repo.update_menu_item(menu_id, menu_data)
if data is None:
return None
menu = data.__dict__ menu = data.__dict__
menu = {k: v for k, v in menu.items() if not k.startswith('_')} menu = {k: v for k, v in menu.items() if not k.startswith('_')}
dishes_conter = 0 dishes_conter = 0

View File

@ -89,14 +89,18 @@ class SubmenuService:
async def update_submenu( async def update_submenu(
self, menu_id: UUID, submenu_id: UUID, submenu_data: MenuBase self, menu_id: UUID, submenu_id: UUID, submenu_data: MenuBase
) -> SubMenuRead: ) -> SubMenuRead | None:
data = await self.submenu_repo.update_submenu_item( data = await self.submenu_repo.update_submenu_item(
menu_id, submenu_id, submenu_data menu_id, submenu_id, submenu_data
) )
if data is None:
return None
submenu = data.__dict__ submenu = data.__dict__
submenu = {k: v for k, v in submenu.items() if not k.startswith('_')} submenu = {k: v for k, v in submenu.items() if not k.startswith('_')}
submenu['dishes_count'] = len(submenu.pop('dishes')) submenu['dishes_count'] = len(submenu.pop('dishes'))
submenu = SubMenuRead(**submenu) submenu = SubMenuRead(**submenu)
await self.cache.set( await self.cache.set(
self.key('submenu', menu_id=str(menu_id), submenu_id=str(submenu_id)), self.key('submenu', menu_id=str(menu_id), submenu_id=str(submenu_id)),
submenu, submenu,

View File

@ -1,11 +1,33 @@
import asyncio import asyncio
import json
import sys import sys
import uvicorn import uvicorn
from fastapi.openapi.utils import get_openapi
from fastfood.app import create_app
from fastfood.repository import create_db_and_tables from fastfood.repository import create_db_and_tables
def create_openapi():
app = create_app()
with open('openapi.json', 'w') as f:
json.dump(
get_openapi(
title=app.title,
version=app.version,
openapi_version=app.openapi_version,
description=app.description,
routes=app.routes,
contact=app.contact,
license_info=app.license_info,
tags=app.openapi_tags,
),
f,
)
def run_app(): def run_app():
""" """
Запуск FastAPI Запуск FastAPI
@ -32,3 +54,6 @@ if __name__ == '__main__':
if '--run-test-server' in sys.argv: if '--run-test-server' in sys.argv:
asyncio.run(recreate()) asyncio.run(recreate())
run_app() run_app()
if 'dump' in sys.argv:
create_openapi()

File diff suppressed because one or more lines are too long