Files
core/orm/reaction.py
Untone 1b48675b92
Some checks failed
Deploy on push / deploy (push) Failing after 2m22s
[0.9.7] - 2025-08-18
### 🔄 Изменения
- **SQLAlchemy KeyError** - исправление ошибки `KeyError: Reaction` при инициализации
- **Исправлена ошибка SQLAlchemy**: Устранена проблема `InvalidRequestError: When initializing mapper Mapper[Shout(shout)], expression Reaction failed to locate a name (Reaction)`

### 🧪 Тестирование
- **Исправление тестов** - адаптация к новой структуре моделей
- **RBAC инициализация** - добавление `rbac.initialize_rbac()` в `conftest.py`
- **Создан тест для getSession**: Добавлен комплексный тест `test_getSession_cookies.py` с проверкой всех сценариев
- **Покрытие edge cases**: Тесты проверяют работу с валидными/невалидными токенами, отсутствующими пользователями
- **Мокирование зависимостей**: Использование unittest.mock для изоляции тестируемого кода

### 🔧 Рефакторинг
- **Упрощена архитектура**: Убраны сложные конструкции с отложенными импортами, заменены на чистую архитектуру
- **Перемещение моделей** - `Author` и связанные модели перенесены в `orm/author.py`: Вынесены базовые модели пользователей (`Author`, `AuthorFollower`, `AuthorBookmark`, `AuthorRating`) из `orm.author` в отдельный модуль
- **Устранены циклические импорты**: Разорван цикл между `auth.core` → `orm.community` → `orm.author` через реструктуризацию архитектуры
- **Создан модуль `utils/password.py`**: Класс `Password` вынесен в utils для избежания циклических зависимостей
- **Оптимизированы импорты моделей**: Убран прямой импорт `Shout` из `orm/community.py`, заменен на строковые ссылки

### 🔧 Авторизация с cookies
- **getSession теперь работает с cookies**: Мутация `getSession` теперь может получать токен из httpOnly cookies даже без заголовка Authorization
- **Убрано требование авторизации**: `getSession` больше не требует декоратор `@login_required`, работает автономно
- **Поддержка dual-авторизации**: Токен может быть получен как из заголовка Authorization, так и из cookie `session_token`
- **Автоматическая установка cookies**: Middleware автоматически устанавливает httpOnly cookies при успешном `getSession`
- **Обновлена GraphQL схема**: `SessionInfo` теперь содержит поля `success`, `error` и опциональные `token`, `author`
- **Единообразная обработка токенов**: Все модули теперь используют централизованные функции для работы с токенами
- **Улучшена обработка ошибок**: Добавлена детальная валидация токенов и пользователей в `getSession`
- **Логирование операций**: Добавлены подробные логи для отслеживания процесса авторизации

### 📝 Документация
- **Обновлена схема GraphQL**: `SessionInfo` тип теперь соответствует новому формату ответа
- Обновлена документация RBAC
- Обновлена документация авторизации с cookies
2025-08-18 14:25:25 +03:00

69 lines
2.3 KiB
Python

import time
from enum import Enum as Enumeration
from sqlalchemy import ForeignKey, Index, Integer, String
from sqlalchemy.orm import Mapped, mapped_column
from orm.base import BaseModel as Base
class ReactionKind(Enumeration):
# TYPE = <reaction index> # rating diff
# editor specials
AGREE = "AGREE" # +1
DISAGREE = "DISAGREE" # -1
# coauthor specials
ASK = "ASK" # 0
PROPOSE = "PROPOSE" # 0
# generic internal reactions
ACCEPT = "ACCEPT" # +1
REJECT = "REJECT" # -1
# experts speacials
PROOF = "PROOF" # +1
DISPROOF = "DISPROOF" # -1
# comment and quote
QUOTE = "QUOTE" # 0
COMMENT = "COMMENT" # 0
# generic rating
LIKE = "LIKE" # +1
DISLIKE = "DISLIKE" # -1
# credit artist or researcher
CREDIT = "CREDIT" # +1
SILENT = "SILENT" # 0
REACTION_KINDS = ReactionKind.__members__.keys()
class Reaction(Base):
__tablename__ = "reaction"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
body: Mapped[str] = mapped_column(String, default="", comment="Reaction Body")
created_at: Mapped[int] = mapped_column(Integer, nullable=False, default=lambda: int(time.time()), index=True)
updated_at: Mapped[int | None] = mapped_column(Integer, nullable=True, comment="Updated at", index=True)
deleted_at: Mapped[int | None] = mapped_column(Integer, nullable=True, comment="Deleted at", index=True)
deleted_by: Mapped[int | None] = mapped_column(ForeignKey("author.id"), nullable=True)
reply_to: Mapped[int | None] = mapped_column(ForeignKey("reaction.id"), nullable=True)
quote: Mapped[str | None] = mapped_column(String, nullable=True, comment="Original quoted text")
shout: Mapped[int] = mapped_column(ForeignKey("shout.id"), nullable=False, index=True)
created_by: Mapped[int] = mapped_column(ForeignKey("author.id"), nullable=False)
kind: Mapped[str] = mapped_column(String, nullable=False, index=True)
oid: Mapped[str | None] = mapped_column(String)
__table_args__ = (
Index("idx_reaction_created_at", "created_at"),
Index("idx_reaction_created_by", "created_by"),
Index("idx_reaction_shout", "shout"),
Index("idx_reaction_kind", "kind"),
{"extend_existing": True},
)