Files
core/resolvers/collection.py
2025-07-31 18:55:59 +03:00

249 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from typing import Any, Optional
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 orm.community import CommunityAuthor
from services.db import local_session
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": "Не удалось определить автора"}
try:
with local_session() as session:
# Исключаем created_by из входных данных - он всегда из токена
filtered_input = {k: v for k, v in collection_input.items() if k != "created_by"}
# Создаем новую коллекцию с обязательным created_by из токена
new_collection = Collection(created_by=author_id, **filtered_input)
session.add(new_collection)
session.commit()
return {"error": None}
except Exception as e:
return {"error": f"Ошибка создания коллекции: {e!s}"}
@mutation.field("update_collection")
@editor_or_admin_required
async def update_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": "Не удалось определить автора"}
slug = collection_input.get("slug")
if not slug:
return {"error": "Не указан slug коллекции"}
try:
with local_session() as session:
# Находим коллекцию для обновления
collection = session.query(Collection).where(Collection.slug == slug).first()
if not collection:
return {"error": "Коллекция не найдена"}
# Проверяем права на редактирование (создатель или админ/редактор)
with local_session() as auth_session:
# Получаем роли пользователя в сообществе
community_author = (
auth_session.query(CommunityAuthor)
.where(
CommunityAuthor.author_id == author_id,
CommunityAuthor.community_id == 1, # Используем сообщество по умолчанию
)
.first()
)
user_roles = community_author.role_list if community_author else []
# Разрешаем редактирование если пользователь - создатель или имеет роль admin/editor
if collection.created_by != author_id and "admin" not in user_roles and "editor" not in user_roles:
return {"error": "Недостаточно прав для редактирования этой коллекции"}
# Обновляем поля коллекции
for key, value in collection_input.items():
# Исключаем изменение created_by - создатель не может быть изменен
if hasattr(collection, key) and key not in ["slug", "created_by"]:
setattr(collection, key, value)
session.commit()
return {"error": None}
except Exception as e:
return {"error": f"Ошибка обновления коллекции: {e!s}"}
@mutation.field("delete_collection")
@editor_or_admin_required
async def delete_collection(_: None, info: GraphQLResolveInfo, slug: str) -> 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": "Не удалось определить автора"}
try:
with local_session() as session:
# Находим коллекцию для удаления
collection = session.query(Collection).where(Collection.slug == slug).first()
if not collection:
return {"error": "Коллекция не найдена"}
# Проверяем права на удаление (создатель или админ/редактор)
with local_session() as auth_session:
# Получаем роли пользователя в сообществе
community_author = (
auth_session.query(CommunityAuthor)
.where(
CommunityAuthor.author_id == author_id,
CommunityAuthor.community_id == 1, # Используем сообщество по умолчанию
)
.first()
)
user_roles = community_author.role_list if community_author else []
# Разрешаем удаление если пользователь - создатель или имеет роль admin/editor
if collection.created_by != author_id and "admin" not in user_roles and "editor" not in user_roles:
return {"error": "Недостаточно прав для удаления этой коллекции"}
# Удаляем связи с публикациями
session.query(ShoutCollection).where(ShoutCollection.collection == collection.id).delete()
# Удаляем коллекцию
session.delete(collection)
session.commit()
return {"error": None}
except Exception as e:
return {"error": f"Ошибка удаления коллекции: {e!s}"}
@type_collection.field("created_by")
def resolve_collection_created_by(obj: Collection, *_: Any) -> Optional[Author]:
"""Резолвер для поля created_by коллекции (может вернуть None)"""
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}")
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()