core/resolvers/collection.py

235 lines
11 KiB
Python
Raw Normal View History

2025-06-30 19:43:32 +00:00
from typing import Any, Optional
2025-06-30 18:46:53 +00:00
from graphql import GraphQLResolveInfo
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.schema import mutation, query, type_collection
@query.field("get_collections_all")
async def get_collections_all(_: None, _info: GraphQLResolveInfo) -> list[Collection]:
"""Получает все коллекции"""
from sqlalchemy.orm import joinedload
with local_session() as session:
# Загружаем коллекции с проверкой существования авторов
collections = (
session.query(Collection)
.options(joinedload(Collection.created_by_author))
.join(
Author,
Collection.created_by == Author.id, # INNER JOIN - исключает коллекции без авторов
)
.filter(
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:
from utils.logger import root_logger as logger
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).filter(Collection.slug == slug).first()
if not collection:
return {"error": "Коллекция не найдена"}
# Проверяем права на редактирование (создатель или админ/редактор)
with local_session() as auth_session:
author = auth_session.query(Author).filter(Author.id == author_id).first()
user_roles = [role.id for role in author.roles] if author and author.roles 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).filter(Collection.slug == slug).first()
if not collection:
return {"error": "Коллекция не найдена"}
# Проверяем права на удаление (создатель или админ/редактор)
with local_session() as auth_session:
author = auth_session.query(Author).filter(Author.id == author_id).first()
user_roles = [role.id for role in author.roles] if author and author.roles 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).filter(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")
2025-06-30 19:43:32 +00:00
def resolve_collection_created_by(obj: Collection, *_: Any) -> Optional[Author]:
"""Резолвер для поля created_by коллекции (может вернуть None)"""
2025-06-30 18:46:53 +00:00
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).filter(Author.id == obj.created_by).first()
if not author:
from utils.logger import root_logger as logger
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:
count = session.query(ShoutCollection).filter(ShoutCollection.collection == obj.id).count()
return count