from typing import Any from graphql import GraphQLResolveInfo from sqlalchemy.orm import joinedload from auth.decorators import editor_or_admin_required from auth.orm import Author from orm.collection import Collection, ShoutCollection from services.db import local_session from services.rbac import require_any_permission from services.schema import mutation, query, type_collection from utils.logger import root_logger as logger @query.field("get_collections_all") async def get_collections_all(_: None, _info: GraphQLResolveInfo) -> list[Collection]: """Получает все коллекции""" with local_session() as session: # Загружаем коллекции с проверкой существования авторов collections = ( session.query(Collection) .options(joinedload(Collection.created_by_author)) .join( Author, Collection.created_by == Author.id, # INNER JOIN - исключает коллекции без авторов ) .where( Collection.created_by.isnot(None), # Дополнительная проверка Author.id.isnot(None), # Проверяем что автор существует ) .all() ) # Дополнительная проверка валидности данных valid_collections = [] for collection in collections: if ( collection.created_by and hasattr(collection, "created_by_author") and collection.created_by_author and collection.created_by_author.id ): valid_collections.append(collection) else: logger.warning(f"Исключена коллекция {collection.id} ({collection.slug}) - проблемы с автором") return valid_collections @query.field("get_collection") async def get_collection(_: None, _info: GraphQLResolveInfo, slug: str) -> Collection | None: """Получает коллекцию по slug""" q = local_session().query(Collection).where(Collection.slug == slug) return q.first() @query.field("get_collections_by_author") async def get_collections_by_author( _: None, _info: GraphQLResolveInfo, slug: str = "", user: str = "", author_id: int = 0 ) -> list[Collection]: """Получает коллекции автора""" with local_session() as session: q = session.query(Collection) if slug: author = session.query(Author).where(Author.slug == slug).first() if author: q = q.where(Collection.created_by == author.id) elif user: author = session.query(Author).where(Author.id == user).first() if author: q = q.where(Collection.created_by == author.id) elif author_id: q = q.where(Collection.created_by == author_id) return q.all() @mutation.field("create_collection") @editor_or_admin_required async def create_collection(_: None, info: GraphQLResolveInfo, collection_input: dict[str, Any]) -> dict[str, Any]: """Создает новую коллекцию""" # Получаем author_id из контекста через декоратор авторизации request = info.context.get("request") author_id = None if hasattr(request, "auth") and request.auth and hasattr(request.auth, "author_id"): author_id = request.auth.author_id elif hasattr(request, "scope") and "auth" in request.scope: auth_info = request.scope.get("auth", {}) if isinstance(auth_info, dict): author_id = auth_info.get("author_id") elif hasattr(auth_info, "author_id"): author_id = auth_info.author_id if not author_id: return {"error": "Не удалось определить автора", "success": False} try: with local_session() as session: # Исключаем created_by из входных данных - он всегда из токена filtered_input = {k: v for k, v in collection_input.items() if k != "created_by"} # Создаем новую коллекцию new_collection = Collection(**filtered_input, created_by=author_id) session.add(new_collection) session.commit() return {"error": None, "success": True} except Exception as e: return {"error": f"Ошибка создания коллекции: {e!s}", "success": False} @mutation.field("update_collection") @require_any_permission(["collection:update", "collection:update_any"]) async def update_collection(_: None, info: GraphQLResolveInfo, collection_input: dict[str, Any]) -> dict[str, Any]: if not collection_input.get("slug"): return {"error": "Не указан slug коллекции", "success": False} try: with local_session() as session: # Находим коллекцию по slug collection = session.query(Collection).where(Collection.slug == collection_input["slug"]).first() if not collection: return {"error": "Коллекция не найдена", "success": False} # Обновляем поля коллекции for key, value in collection_input.items(): if hasattr(collection, key) and key not in ["slug", "created_by"]: setattr(collection, key, value) session.commit() return {"error": None, "success": True} except Exception as e: return {"error": f"Ошибка обновления коллекции: {e!s}", "success": False} @mutation.field("delete_collection") @require_any_permission(["collection:delete", "collection:delete_any"]) async def delete_collection(_: None, info: GraphQLResolveInfo, slug: str) -> dict[str, Any]: try: with local_session() as session: # Находим коллекцию по slug collection = session.query(Collection).where(Collection.slug == slug).first() if not collection: return {"error": "Коллекция не найдена", "success": False} # Удаляем коллекцию session.delete(collection) session.commit() return {"error": None, "success": True} except Exception as e: return {"error": f"Ошибка удаления коллекции: {e!s}", "success": False} @type_collection.field("created_by") def resolve_collection_created_by(obj: Collection, *_: Any) -> Author: """Резолвер для поля created_by коллекции""" with local_session() as session: if hasattr(obj, "created_by_author") and obj.created_by_author: return obj.created_by_author author = session.query(Author).where(Author.id == obj.created_by).first() if not author: logger.warning(f"Автор с ID {obj.created_by} не найден для коллекции {obj.id}") # Возвращаем заглушку вместо None return Author( id=obj.created_by or 0, name=f"Unknown User {obj.created_by or 0}", slug=f"user-{obj.created_by or 0}", email="unknown@example.com", ) return author @type_collection.field("amount") def resolve_collection_amount(obj: Collection, *_: Any) -> int: """Резолвер для количества публикаций в коллекции""" with local_session() as session: return session.query(ShoutCollection).where(ShoutCollection.collection == obj.id).count()