diff --git a/src/apps/remd/v1/router.py b/src/apps/remd/v1/router.py index efb3fad..05622af 100644 --- a/src/apps/remd/v1/router.py +++ b/src/apps/remd/v1/router.py @@ -13,13 +13,14 @@ router = APIRouter( @router.post('/callback') async def callback(request: Request): - if not request.headers.get('content-type', '').startswith( - 'application/xml' - ): - logger.warning('Content-Type must be application/xml') - raise HTTPException( - status_code=400, detail='Content-Type must be application/xml' - ) + logger.info(request.headers.get('content-type', '')) + # if not request.headers.get('content-type', '').startswith( + # 'application/xml' + # ): + # logger.warning('Content-Type must be application/xml') + # raise HTTPException( + # status_code=400, detail='Content-Type must be application/xml' + # ) body_bytes = await request.body() if not body_bytes: diff --git a/src/apps/users/v1/router.py b/src/apps/users/v1/router.py index 431d728..032782a 100644 --- a/src/apps/users/v1/router.py +++ b/src/apps/users/v1/router.py @@ -14,6 +14,7 @@ from clients import clients as c from clients.tmk import schema as ts from clients.vitacore import schema as vs from shared import exceptions as e +from shared.redis import cache_response from shared.redis import client as cache from . import schema as s @@ -27,7 +28,13 @@ router = APIRouter( ) -@router.get('/getProfile', response_model=vs.ProfileModel) +@cache_response(ttl=600, namespace='main') +@router.get( + '/getProfile', + responses={ + status.HTTP_200_OK: {'model': vs.ProfileModel}, + }, +) async def get_profile(user: Annotated[User, Depends(login)]): """ Get profile of user. @@ -35,6 +42,7 @@ async def get_profile(user: Annotated[User, Depends(login)]): return await c.vitacore_api.getProfile(user.vita_id) +@cache_response(ttl=3600, namespace='main') @router.get( '/getDepartments', responses={ @@ -48,7 +56,10 @@ async def get_departments(): return await c.vitacore_api.getDepartments() -@router.get('/getWorkers', response_model=vs.WorkersModel) +@cache_response(ttl=3600, namespace='main') +@router.get( + '/getWorkers', responses={status.HTTP_200_OK: {'model': vs.WorkersModel}} +) async def get_workers( user: Annotated[User, Depends(login)], departmentId: str ): @@ -58,7 +69,11 @@ async def get_workers( return await c.vitacore_api.getWorkers(departmentId) -@router.get('/getSpecs', response_model=vs.SpecsV021Model) +@cache_response(ttl=3600, namespace='main') +@router.get( + '/getSpecs', + responses={status.HTTP_200_OK: {'model': vs.SpecsV021Model}}, +) async def get_specs(user: Annotated[User, Depends(login)]): """ Get list of specialties. @@ -66,7 +81,9 @@ async def get_specs(user: Annotated[User, Depends(login)]): return await c.vitacore_api.getSpecsV021() -@router.get('/getEntries', response_model=vs.EntriesModel) +@router.get( + '/getEntries', responses={status.HTTP_200_OK: {'model': vs.EntriesModel}} +) async def get_entries(user: Annotated[User, Depends(login)]): """ Get list of entries for user by id. diff --git a/src/shared/redis.py b/src/shared/redis.py index ab85560..b558a9f 100644 --- a/src/shared/redis.py +++ b/src/shared/redis.py @@ -1,5 +1,45 @@ +from collections.abc import Callable, Coroutine +from functools import wraps +from typing import Any, TypeVar + +from fastapi.responses import ORJSONResponse +from orjson import dumps, loads from redis.asyncio import Redis +from apps.users.models import User from core.config import settings client = Redis.from_url(settings.REDIS_URL) +T = TypeVar('T') + + +def cache_response(ttl: int = 60, namespace: str = 'main'): + def decorator( + func: Callable[..., Coroutine[Any, Any, T]], + ) -> Callable[..., Coroutine[Any, Any, T | ORJSONResponse]]: + @wraps(func) + async def wrapper(*args: Any, **kwargs: Any): + cache_key = f'endpoints:{namespace}_{func.__name__}' + user = kwargs.get('user') + user_id = kwargs.get('user_id') + + if user_id: + cache_key += f':{user_id}' + + if isinstance(user, User): + cache_key += f':{user.id}' + + cached_value = await client.get(cache_key) + if cached_value: + return ORJSONResponse( + content=loads(cached_value), headers={'X-Cache': 'hit'} + ) + + response = await func(*args, **kwargs) + await client.set(cache_key, dumps(response), ex=ttl) + + return response + + return wrapper + + return decorator