from aiohttp import web from aiohttp_apispec import docs, headers_schema, json_schema, querystring_schema from apollo_utils.service.clients.aiohttp.utils.response import dump_response_schema from sqlalchemy import or_ from server.constants.core import APOLLO_SLUG, BASE_API_PREFIX from server.constants.favorites import FavoritesModifyInclude from server.db.models.users import Favorites from server.domains.accounts import get_account from server.schemas.favorites.core import FavoritesCreateRequest, FavoritesDeleteRequest, FavoritesGet, \ FavoritesResponse, validate_apollo_entity from server.schemas.favorites.exceptions import AlreadyAddedToFavoritesError, AlreadyRemovedFromFavoritesError from server.schemas.headers import AccountHeader from server.utils.favorites import add_tag_filters from server.utils.pagination import paginate router = web.RouteTableDef() @router.get(BASE_API_PREFIX + "/users/favorites/") @docs( tags=["favorites"], summary="Get favorite entities by an account.", ) @headers_schema(AccountHeader) @querystring_schema(FavoritesGet.Request) @dump_response_schema(FavoritesGet.Response, apply=True) @paginate() async def get_favorites(request: web.Request) -> web.Response: headers, params = request["headers"], request["querystring"] params["order_by"] = Favorites.created_at.desc() entity_type_list, entity_id_list = params.pop("entity_type_list", None), params.pop("entity_id", None) tag_id = params.pop("tag_id", []) filters = [] if entity_type_list: filters.append(Favorites.entity_type.in_(entity_type_list)) if entity_id_list: filters.append(Favorites.entity_id.in_(entity_id_list)) if tag_id: add_tag_filters(filters, tag_id=tag_id) q = Favorites.q_filter_by_account(app_slug=headers["app"], user_id=headers["user_id"]) result = await Favorites.list(q, filters=filters, **params) return result @router.post(BASE_API_PREFIX + "/users/favorites/") @docs( tags=["favorites"], summary="Add an entity to favorites.", ) @headers_schema(AccountHeader) @json_schema(FavoritesCreateRequest) @dump_response_schema(FavoritesResponse, code=201, apply=False) async def add_to_favorites(request: web.Request) -> web.Response: headers, data = request["headers"], request["json"] app_slug, user_id, include = headers["app"], headers["user_id"], data.get("include") if app_slug == APOLLO_SLUG: data = validate_apollo_entity(data) account, _ = await get_account(app_slug=app_slug, user_id=user_id) account_id = account.id entity_id, entity_type = data["entity_id"], data["entity_type"] is_already_favorite = await Favorites.get(account_id=account_id, entity_id=entity_id, entity_type=entity_type) if is_already_favorite: raise AlreadyAddedToFavoritesError result = await Favorites.create( dict( entity_id=entity_id, entity_type=entity_type, data=data["data"], account_id=account_id, ) ) schema = FavoritesResponse() if include == FavoritesModifyInclude.COUNT.value: count = await Favorites.count(filters=[ Favorites.account_id == account_id, Favorites.entity_type ==entity_type ]) schema.context["count"] = count return schema.dump(result) @router.delete(BASE_API_PREFIX + "/users/favorites/") @docs( tags=["favorites"], summary="Remove one or multiple entities from favorites by id(s).", ) @headers_schema(AccountHeader) @querystring_schema(FavoritesDeleteRequest) async def remove_from_favorites_by_id(request: web.Request) -> web.Response: headers, data, count = request["headers"], request["querystring"], None favorite_ids, entity_ids = data.get("id") or [], data.get("entity_id") or [] filters = [Favorites.account_id == Favorites.q_account_id(app_slug=headers["app"], user_id=headers["user_id"])] if favorite_ids and entity_ids: filters.append(or_(Favorites.id.in_(favorite_ids), Favorites.entity_id.in_(entity_ids))) elif favorite_ids: filters.append(Favorites.id.in_(favorite_ids)) elif entity_ids: filters.append(Favorites.entity_id.in_(entity_ids)) removed = await Favorites.delete(filters=filters, return_source=Favorites) if FavoritesModifyInclude.COUNT.value in data.get("include", []): filters = [Favorites.account_id == Favorites.q_account_id(app_slug=headers["app"], user_id=headers["user_id"])] entity_type_list = data.get("entity_type_list") if entity_type_list: filters.append(Favorites.entity_type.in_(entity_type_list)) count = await Favorites.count(filters=filters) if len(favorite_ids + entity_ids) != len(removed): raise AlreadyRemovedFromFavoritesError(extra={"entity_id": [rf.entity_id for rf in removed], "count": count}) return web.json_response({"count": count})