From 68fce283bc048ae72a76a2e13c4f6a2871d2ddd6 Mon Sep 17 00:00:00 2001 From: Untone Date: Tue, 28 Nov 2023 19:04:45 +0300 Subject: [PATCH] authorid-context --- orm/collection.py | 25 ------------- orm/community.py | 41 --------------------- orm/reaction.py | 43 ---------------------- orm/shout.py | 86 ------------------------------------------- orm/topic.py | 26 ------------- pyproject.toml | 3 ++ resolvers/listener.py | 3 -- resolvers/schema.py | 69 ++++++++++++++++------------------ services/auth.py | 26 +++++-------- services/core.py | 25 ++----------- 10 files changed, 47 insertions(+), 300 deletions(-) delete mode 100644 orm/collection.py delete mode 100644 orm/community.py delete mode 100644 orm/reaction.py delete mode 100644 orm/shout.py delete mode 100644 orm/topic.py diff --git a/orm/collection.py b/orm/collection.py deleted file mode 100644 index 0fb6167..0000000 --- a/orm/collection.py +++ /dev/null @@ -1,25 +0,0 @@ -import time - -from sqlalchemy import Column, ForeignKey, Integer, String - -from services.db import Base - - -class ShoutCollection(Base): - __tablename__ = "shout_collection" - - id = None # type: ignore - shout = Column(ForeignKey("shout.id"), primary_key=True) - collection = Column(ForeignKey("collection.id"), primary_key=True) - - -class Collection(Base): - __tablename__ = "collection" - - slug = Column(String, unique=True) - title = Column(String, nullable=False, comment="Title") - body = Column(String, nullable=True, comment="Body") - pic = Column(String, nullable=True, comment="Picture") - created_at = Column(Integer, default=lambda: int(time.time())) - created_by = Column(ForeignKey("author.id"), comment="Created By") - publishedAt = Column(Integer, default=lambda: int(time.time())) diff --git a/orm/community.py b/orm/community.py deleted file mode 100644 index b549725..0000000 --- a/orm/community.py +++ /dev/null @@ -1,41 +0,0 @@ -import time - -from sqlalchemy import Column, ForeignKey, Integer, String -from sqlalchemy.orm import relationship - -from orm.author import Author -from services.db import Base, local_session - - -class CommunityAuthor(Base): - __tablename__ = "community_author" - - id = None # type: ignore - follower = Column(ForeignKey("author.id"), primary_key=True) - community = Column(ForeignKey("community.id"), primary_key=True) - joined_at = Column(Integer, nullable=False, default=lambda: int(time.time())) - role = Column(String, nullable=False) - - -class Community(Base): - __tablename__ = "community" - - name = Column(String, nullable=False) - slug = Column(String, nullable=False, unique=True) - desc = Column(String, nullable=False, default="") - pic = Column(String, nullable=False, default="") - created_at = Column(Integer, nullable=False, default=lambda: int(time.time())) - - authors = relationship(lambda: Author, secondary=CommunityAuthor.__tablename__) - - @staticmethod - def init_table(): - with local_session("orm.community") as session: - d = session.query(Community).filter(Community.slug == "discours").first() - if not d: - d = Community(name="Дискурс", slug="discours") - session.add(d) - session.commit() - print("[orm.community] created community %s" % d.slug) - Community.default_community = d - print("[orm.community] default community is %s" % d.slug) diff --git a/orm/reaction.py b/orm/reaction.py deleted file mode 100644 index c45df75..0000000 --- a/orm/reaction.py +++ /dev/null @@ -1,43 +0,0 @@ -import time -from enum import Enum as Enumeration - -from sqlalchemy import Column, Enum, ForeignKey, Integer, String - -from services.db import Base - - -class ReactionKind(Enumeration): - # TYPE = # rating diff - - # editor mode - AGREE = 1 # +1 - DISAGREE = 2 # -1 - ASK = 3 # +0 - PROPOSE = 4 # +0 - PROOF = 5 # +1 - DISPROOF = 6 # -1 - ACCEPT = 7 # +1 - REJECT = 8 # -1 - - # public feed - QUOTE = 9 # +0 - COMMENT = 0 # +0 - LIKE = 11 # +1 - DISLIKE = 12 # -1 - - -class Reaction(Base): - __tablename__ = "reaction" - - body = Column(String, default='', comment="Reaction Body") - created_at = Column(Integer, nullable=False, default=lambda: int(time.time())) - created_by = Column(ForeignKey("author.id"), nullable=False, index=True) - updated_at = Column(Integer, nullable=True, comment="Updated at") - deleted_at = Column(Integer, nullable=True, comment="Deleted at") - deleted_by = Column(ForeignKey("author.id"), nullable=True, index=True) - shout = Column(ForeignKey("shout.id"), nullable=False, index=True) - reply_to = Column(ForeignKey("reaction.id"), nullable=True) - quote = Column(String, nullable=True, comment="Original quoted text") - kind = Column(Enum(ReactionKind), nullable=False) - - oid = Column(String) diff --git a/orm/shout.py b/orm/shout.py deleted file mode 100644 index f402d7c..0000000 --- a/orm/shout.py +++ /dev/null @@ -1,86 +0,0 @@ -import time -from enum import Enum as Enumeration - -from sqlalchemy import JSON, Boolean, Column, Enum, ForeignKey, Integer, String -from sqlalchemy.orm import relationship - -from orm.author import Author -from orm.community import Community -from orm.reaction import Reaction -from orm.topic import Topic -from services.db import Base - - -class ShoutTopic(Base): - __tablename__ = "shout_topic" - - id = None # type: ignore - shout = Column(ForeignKey("shout.id"), primary_key=True, index=True) - topic = Column(ForeignKey("topic.id"), primary_key=True, index=True) - - -class ShoutReactionsFollower(Base): - __tablename__ = "shout_reactions_followers" - - id = None # type: ignore - follower = Column(ForeignKey("author.id"), primary_key=True, index=True) - shout = Column(ForeignKey("shout.id"), primary_key=True, index=True) - auto = Column(Boolean, nullable=False, default=False) - created_at = Column(Integer, nullable=False, default=lambda: int(time.time())) - deleted_at = Column(Integer, nullable=True) - - -class ShoutAuthor(Base): - __tablename__ = "shout_author" - - id = None # type: ignore - shout = Column(ForeignKey("shout.id"), primary_key=True, index=True) - author = Column(ForeignKey("author.id"), primary_key=True, index=True) - caption = Column(String, nullable=True, default="") - - -class ShoutCommunity(Base): - __tablename__ = "shout_community" - - id = None # type: ignore - shout = Column(ForeignKey("shout.id"), primary_key=True, index=True) - community = Column(ForeignKey("community.id"), primary_key=True, index=True) - - -class ShoutVisibility(Enumeration): - AUTHORS = 0 - COMMUNITY = 1 - PUBLIC = 2 - - -class Shout(Base): - __tablename__ = "shout" - - created_at = Column(Integer, nullable=False, default=lambda: int(time.time())) - updated_at = Column(Integer, nullable=True) - published_at = Column(Integer, nullable=True) - deleted_at = Column(Integer, nullable=True) - - deleted_by = Column(ForeignKey("author.id"), nullable=True) - - body = Column(String, nullable=False, comment="Body") - slug = Column(String, unique=True) - cover = Column(String, nullable=True, comment="Cover image url") - lead = Column(String, nullable=True) - description = Column(String, nullable=True) - title = Column(String, nullable=True) - subtitle = Column(String, nullable=True) - layout = Column(String, nullable=True) - media = Column(JSON, nullable=True) - - authors = relationship(lambda: Author, secondary="shout_author") - topics = relationship(lambda: Topic, secondary="shout_topic") - communities = relationship(lambda: Community, secondary="shout_community") - reactions = relationship(lambda: Reaction) - - visibility = Column(Enum(ShoutVisibility), default=ShoutVisibility.AUTHORS) - - lang = Column(String, nullable=False, default="ru", comment="Language") - version_of = Column(ForeignKey("shout.id"), nullable=True) - oid = Column(String, nullable=True) - diff --git a/orm/topic.py b/orm/topic.py deleted file mode 100644 index 96d4d18..0000000 --- a/orm/topic.py +++ /dev/null @@ -1,26 +0,0 @@ -import time - -from sqlalchemy import Boolean, Column, ForeignKey, Integer, String - -from services.db import Base - - -class TopicFollower(Base): - __tablename__ = "topic_followers" - - id = None # type: ignore - follower = Column(ForeignKey("author.id"), primary_key=True, index=True) - topic = Column(ForeignKey("topic.id"), primary_key=True, index=True) - created_at = Column(Integer, nullable=False, default=lambda: int(time.time())) - auto = Column(Boolean, nullable=False, default=False) - - -class Topic(Base): - __tablename__ = "topic" - - slug = Column(String, unique=True) - title = Column(String, nullable=False, comment="Title") - body = Column(String, nullable=True, comment="Body") - pic = Column(String, nullable=True, comment="Picture") - community = Column(ForeignKey("community.id"), default=1) - oid = Column(String, nullable=True, comment="Old ID") diff --git a/pyproject.toml b/pyproject.toml index b91509a..a2eee30 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,9 @@ black = { version = "^23.9.1", python = ">=3.12" } mypy = { version = "^1.7", python = ">=3.12" } setuptools = "^69.0.2" +[tool.poetry.group.dev.dependencies] +setuptools = "^69.0.2" + [tool.black] line-length = 120 target-version = ['py312'] diff --git a/resolvers/listener.py b/resolvers/listener.py index ab48783..dfca4fa 100644 --- a/resolvers/listener.py +++ b/resolvers/listener.py @@ -1,6 +1,3 @@ -import json -import asyncio - from orm.notification import Notification from services.db import local_session from services.rediscache import redis diff --git a/resolvers/schema.py b/resolvers/schema.py index efbc873..4b91692 100644 --- a/resolvers/schema.py +++ b/resolvers/schema.py @@ -35,15 +35,16 @@ class NotificationsResult: total: int -def get_notifications(author, session, limit, offset) -> List[Notification]: +def get_notifications(author_id, session, limit, offset) -> List[Notification]: NotificationSeenAlias = aliased(NotificationSeen) - query = select( - NotificationMessage, - NotificationSeenAlias.viewer.label("seen") - ).outerjoin( - NotificationSeen, - and_(NotificationSeen.viewer == author.id, NotificationSeen.notification == NotificationMessage.id), - ).group_by(NotificationSeen.notification) + query = ( + select(NotificationMessage, NotificationSeenAlias.viewer.label("seen")) + .outerjoin( + NotificationSeen, + and_(NotificationSeen.viewer == author_id, NotificationSeen.notification == NotificationMessage.id), + ) + .group_by(NotificationSeen.notification) + ) if limit: query = query.limit(limit) if offset: @@ -69,26 +70,21 @@ class Query: @strawberry.field @login_required async def load_notifications(self, info, limit: int = 50, offset: int = 0) -> NotificationsResult: - user_id = info.context["user_id"] + author_id = info.context.get("author_id") with local_session() as session: try: - author = session.query(Author).filter(Author.user == user_id).first() - if author: - notifications = get_notifications(author, session, limit, offset) + if author_id: + notifications = get_notifications(author_id, session, limit, offset) if notifications and len(notifications) > 0: nr = NotificationsResult( notifications=notifications, - unread=sum(1 for n in notifications if author.id in n.seen), - total=session.query(NotificationMessage).count() + unread=sum(1 for n in notifications if author_id in n.seen), + total=session.query(NotificationMessage).count(), ) return nr except Exception as ex: print(f"[resolvers.schema] {ex}") - return NotificationsResult( - notifications=[], - total=0, - unread=0 - ) + return NotificationsResult(notifications=[], total=0, unread=0) @strawberry.type @@ -96,34 +92,31 @@ class Mutation: @strawberry.mutation @login_required async def mark_notification_as_read(self, info, notification_id: int) -> NotificationSeenResult: - user_id = info.context["user_id"] - with local_session() as session: - try: - author = session.query(Author).filter(Author.user == user_id).first() - if author: - ns = NotificationSeen(notification=notification_id, viewer=author.id) + author_id = info.context.get("author_id") + if author_id: + with local_session() as session: + try: + ns = NotificationSeen(notification=notification_id, viewer=author_id) session.add(ns) session.commit() - except SQLAlchemyError as e: - session.rollback() - print(f"[mark_notification_as_read] error: {str(e)}") - nsr = NotificationSeenResult(error="cant mark as read") - return nsr + except SQLAlchemyError as e: + session.rollback() + print(f"[mark_notification_as_read] error: {str(e)}") + nsr = NotificationSeenResult(error="cant mark as read") + return nsr return NotificationSeenResult() @strawberry.mutation @login_required async def mark_all_notifications_as_read(self, info) -> NotificationSeenResult: - user_id = info.context["user_id"] - - with local_session() as session: + author_id = info.context.get("author_id") + if author_id: try: - author = session.query(Author).filter(Author.user == user_id).first() - if author: - nslist = get_notifications(author, session, None, None) + with local_session() as session: + nslist = get_notifications(author_id, session, None, None) for n in nslist: - if author.id not in n.seen: - ns = NotificationSeen(viewer=author.id, notification=n.id) + if author_id not in n.seen: + ns = NotificationSeen(viewer=author_id, notification=n.id) session.add(ns) session.commit() except SQLAlchemyError as e: diff --git a/services/auth.py b/services/auth.py index 3381183..f6f1056 100644 --- a/services/auth.py +++ b/services/auth.py @@ -1,5 +1,8 @@ from functools import wraps -from httpx import AsyncClient, HTTPError +from httpx import AsyncClient + +from orm.author import Author +from services.db import local_session from settings import AUTH_URL @@ -42,23 +45,14 @@ def login_required(f): raise Exception("You are not logged in") else: # Добавляем author_id в контекст - context["user_id"] = user_id + with local_session() as session: + author = session.query(Author).filter(Author.user == user_id).first() + if author: + context["author_id"] = author.id + if user_id: + context["user_id"] = user_id # Если пользователь аутентифицирован, выполняем резолвер return await f(*args, **kwargs) return decorated_function - - -def auth_request(f): - @wraps(f) - async def decorated_function(*args, **kwargs): - req = args[0] - is_authenticated, user_id = await check_auth(req) - if not is_authenticated: - raise HTTPError("please, login first") - else: - req["user_id"] = user_id - return await f(*args, **kwargs) - - return decorated_function diff --git a/services/core.py b/services/core.py index d22e8fe..50b0ba7 100644 --- a/services/core.py +++ b/services/core.py @@ -1,9 +1,5 @@ -from typing import List - +from typing import List, Any from httpx import AsyncClient - -from orm.author import Author -from orm.shout import Shout from settings import API_BASE headers = {"Content-Type": "application/json"} @@ -27,22 +23,7 @@ async def _request_endpoint(query_name, body): traceback.print_exc() -async def get_author(author_id) -> Author: - query_name = "get_author" - query_type = "query" - operation = "GetAuthor" - query_fields = "id slug pic name" - - gql = { - "query": query_type + " " + operation + " { " + query_name + " { " + query_fields + "} " + " }", - "operationName": operation, - "variables": None, - } - - return await _request_endpoint(query_name, gql) - - -async def get_followed_shouts(author_id: int) -> List[Shout]: +async def get_followed_shouts(author_id: int) -> List[Any]: query_name = "load_shouts_followed" query_type = "query" operation = "GetFollowedShouts" @@ -55,7 +36,7 @@ async def get_followed_shouts(author_id: int) -> List[Shout]: body = { "query": query, "operationName": operation, - "variables": {"author_id": author_id, "limit": 1000, "offset": 0}, # FIXME: too big + "variables": {"author_id": author_id, "limit": 1000, "offset": 0}, # FIXME: too big limit } return await _request_endpoint(query_name, body)