0.4.9-drafts
This commit is contained in:
parent
dce05342df
commit
37a9a284ef
|
@ -1,3 +1,9 @@
|
|||
#### [0.4.9] - 2025-02-09
|
||||
- `Shout.draft` field added
|
||||
- `Draft` entity added
|
||||
- `create_draft`, `update_draft`, `delete_draft` mutations and resolvers added
|
||||
- `get_shout_drafts` resolver updated
|
||||
|
||||
#### [0.4.8] - 2025-02-03
|
||||
- `Reaction.deleted_at` filter on `update_reaction` resolver added
|
||||
- `triggers` module updated with `after_shout_handler`, `after_reaction_handler` for cache revalidation
|
||||
|
|
|
@ -44,8 +44,8 @@ Start API server with `dev` keyword added and `mkcert` installed:
|
|||
|
||||
```shell
|
||||
mkdir .venv
|
||||
python3.12 -m venv .venv
|
||||
poetry env use .venv/bin/python3.12
|
||||
python3.12 -m venv venv
|
||||
poetry env use venv/bin/python3.12
|
||||
poetry update
|
||||
|
||||
mkcert -install
|
||||
|
|
33
cache/cache.py
vendored
33
cache/cache.py
vendored
|
@ -156,17 +156,17 @@ async def get_cached_authors_by_ids(author_ids: List[int]) -> List[dict]:
|
|||
async def get_cached_topic_followers(topic_id: int):
|
||||
"""
|
||||
Получает подписчиков темы по ID, используя кеш Redis.
|
||||
|
||||
|
||||
Args:
|
||||
topic_id: ID темы
|
||||
|
||||
|
||||
Returns:
|
||||
List[dict]: Список подписчиков с их данными
|
||||
"""
|
||||
try:
|
||||
cache_key = CACHE_KEYS["TOPIC_FOLLOWERS"].format(topic_id)
|
||||
cached = await redis_operation("GET", cache_key)
|
||||
|
||||
|
||||
if cached:
|
||||
followers_ids = json.loads(cached)
|
||||
logger.debug(f"Found {len(followers_ids)} cached followers for topic #{topic_id}")
|
||||
|
@ -174,12 +174,13 @@ async def get_cached_topic_followers(topic_id: int):
|
|||
|
||||
with local_session() as session:
|
||||
followers_ids = [
|
||||
f[0] for f in session.query(Author.id)
|
||||
f[0]
|
||||
for f in session.query(Author.id)
|
||||
.join(TopicFollower, TopicFollower.follower == Author.id)
|
||||
.filter(TopicFollower.topic == topic_id)
|
||||
.all()
|
||||
]
|
||||
|
||||
|
||||
await redis_operation("SETEX", cache_key, value=json.dumps(followers_ids), ttl=CACHE_TTL)
|
||||
followers = await get_cached_authors_by_ids(followers_ids)
|
||||
logger.debug(f"Cached {len(followers)} followers for topic #{topic_id}")
|
||||
|
@ -405,7 +406,7 @@ async def cache_related_entities(shout: Shout):
|
|||
async def invalidate_shout_related_cache(shout: Shout, author_id: int):
|
||||
"""
|
||||
Инвалидирует весь кэш, связанный с публикацией и её связями
|
||||
|
||||
|
||||
Args:
|
||||
shout: Объект публикации
|
||||
author_id: ID автора
|
||||
|
@ -418,22 +419,14 @@ async def invalidate_shout_related_cache(shout: Shout, author_id: int):
|
|||
"recent", # последние
|
||||
"coauthored", # совместные
|
||||
}
|
||||
|
||||
|
||||
# Добавляем ключи авторов
|
||||
cache_keys.update(
|
||||
f"author_{a.id}" for a in shout.authors
|
||||
)
|
||||
cache_keys.update(
|
||||
f"authored_{a.id}" for a in shout.authors
|
||||
)
|
||||
|
||||
cache_keys.update(f"author_{a.id}" for a in shout.authors)
|
||||
cache_keys.update(f"authored_{a.id}" for a in shout.authors)
|
||||
|
||||
# Добавляем ключи тем
|
||||
cache_keys.update(
|
||||
f"topic_{t.id}" for t in shout.topics
|
||||
)
|
||||
cache_keys.update(
|
||||
f"topic_shouts_{t.id}" for t in shout.topics
|
||||
)
|
||||
cache_keys.update(f"topic_{t.id}" for t in shout.topics)
|
||||
cache_keys.update(f"topic_shouts_{t.id}" for t in shout.topics)
|
||||
|
||||
await invalidate_shouts_cache(list(cache_keys))
|
||||
|
||||
|
|
56
orm/draft.py
Normal file
56
orm/draft.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
import time
|
||||
|
||||
from sqlalchemy import JSON, Boolean, Column, ForeignKey, Integer, String
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from orm.author import Author
|
||||
from orm.topic import Topic
|
||||
from services.db import Base
|
||||
|
||||
|
||||
class DraftTopic(Base):
|
||||
__tablename__ = "draft_topic"
|
||||
|
||||
id = None # type: ignore
|
||||
shout = Column(ForeignKey("draft.id"), primary_key=True, index=True)
|
||||
topic = Column(ForeignKey("topic.id"), primary_key=True, index=True)
|
||||
main = Column(Boolean, nullable=True)
|
||||
|
||||
|
||||
class DraftAuthor(Base):
|
||||
__tablename__ = "draft_author"
|
||||
|
||||
id = None # type: ignore
|
||||
shout = Column(ForeignKey("draft.id"), primary_key=True, index=True)
|
||||
author = Column(ForeignKey("author.id"), primary_key=True, index=True)
|
||||
caption = Column(String, nullable=True, default="")
|
||||
|
||||
|
||||
class Draft(Base):
|
||||
__tablename__ = "draft"
|
||||
|
||||
created_at: int = Column(Integer, nullable=False, default=lambda: int(time.time()))
|
||||
updated_at: int | None = Column(Integer, nullable=True, index=True)
|
||||
deleted_at: int | None = Column(Integer, nullable=True, index=True)
|
||||
|
||||
body: str = Column(String, nullable=False, comment="Body")
|
||||
slug: str = Column(String, unique=True)
|
||||
cover: str | None = Column(String, nullable=True, comment="Cover image url")
|
||||
cover_caption: str | None = Column(String, nullable=True, comment="Cover image alt caption")
|
||||
lead: str | None = Column(String, nullable=True)
|
||||
description: str | None = Column(String, nullable=True)
|
||||
title: str = Column(String, nullable=False)
|
||||
subtitle: str | None = Column(String, nullable=True)
|
||||
layout: str = Column(String, nullable=False, default="article")
|
||||
media: dict | None = Column(JSON, nullable=True)
|
||||
|
||||
lang: str = Column(String, nullable=False, default="ru", comment="Language")
|
||||
oid: str | None = Column(String, nullable=True)
|
||||
seo: str | None = Column(String, nullable=True) # JSON
|
||||
|
||||
created_by: int = Column(ForeignKey("author.id"), nullable=False)
|
||||
updated_by: int | None = Column(ForeignKey("author.id"), nullable=True)
|
||||
deleted_by: int | None = Column(ForeignKey("author.id"), nullable=True)
|
||||
authors = relationship(Author, secondary="draft_author")
|
||||
topics = relationship(Topic, secondary="draft_topic")
|
||||
shout: int | None = Column(ForeignKey("shout.id"), nullable=True)
|
|
@ -72,3 +72,5 @@ class Shout(Base):
|
|||
oid: str | None = Column(String, nullable=True)
|
||||
|
||||
seo: str | None = Column(String, nullable=True) # JSON
|
||||
|
||||
draft: int | None = Column(ForeignKey("draft.id"), nullable=True)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "core"
|
||||
version = "0.4.8"
|
||||
version = "0.4.9"
|
||||
description = "core module for discours.io"
|
||||
authors = ["discoursio devteam"]
|
||||
license = "MIT"
|
||||
|
@ -32,6 +32,8 @@ authlib = "^1.3.2"
|
|||
ruff = "^0.4.7"
|
||||
isort = "^5.13.2"
|
||||
pydantic = "^2.9.2"
|
||||
pytest = "^8.3.4"
|
||||
mypy = "^1.15.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
|
@ -39,7 +41,7 @@ build-backend = "poetry.core.masonry.api"
|
|||
|
||||
[tool.pyright]
|
||||
venvPath = "."
|
||||
venv = ".venv"
|
||||
venv = "venv"
|
||||
|
||||
[tool.isort]
|
||||
multi_line_output = 3
|
||||
|
|
|
@ -11,6 +11,14 @@ from resolvers.author import ( # search_authors,
|
|||
update_author,
|
||||
)
|
||||
from resolvers.community import get_communities_all, get_community
|
||||
from resolvers.draft import (
|
||||
create_draft,
|
||||
delete_draft,
|
||||
load_drafts,
|
||||
publish_draft,
|
||||
unpublish_draft,
|
||||
update_draft,
|
||||
)
|
||||
from resolvers.editor import create_shout, delete_shout, update_shout
|
||||
from resolvers.feed import (
|
||||
load_shouts_coauthored,
|
||||
|
@ -113,4 +121,12 @@ __all__ = [
|
|||
"rate_author",
|
||||
"get_my_rates_comments",
|
||||
"get_my_rates_shouts",
|
||||
# draft
|
||||
"load_drafts",
|
||||
"create_draft",
|
||||
"update_draft",
|
||||
"delete_draft",
|
||||
"publish_draft",
|
||||
"publish_shout",
|
||||
"unpublish_shout",
|
||||
]
|
||||
|
|
230
resolvers/draft.py
Normal file
230
resolvers/draft.py
Normal file
|
@ -0,0 +1,230 @@
|
|||
import time
|
||||
from importlib import invalidate_caches
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from cache.cache import invalidate_shout_related_cache, invalidate_shouts_cache
|
||||
from orm.author import Author
|
||||
from orm.draft import Draft
|
||||
from orm.shout import Shout
|
||||
from services.auth import login_required
|
||||
from services.db import local_session
|
||||
from services.schema import mutation, query
|
||||
from utils.logger import root_logger as logger
|
||||
|
||||
|
||||
@query.field("load_drafts")
|
||||
@login_required
|
||||
async def load_drafts(_, info):
|
||||
user_id = info.context.get("user_id")
|
||||
author_dict = info.context.get("author", {})
|
||||
author_id = author_dict.get("id")
|
||||
|
||||
if not user_id or not author_id:
|
||||
return {"error": "User ID and author ID are required"}
|
||||
|
||||
with local_session() as session:
|
||||
drafts = session.query(Draft).filter(Draft.authors.any(Author.id == author_id)).all()
|
||||
return {"drafts": drafts}
|
||||
|
||||
|
||||
@mutation.field("create_draft")
|
||||
@login_required
|
||||
async def create_draft(_, info, shout_id: int = 0):
|
||||
user_id = info.context.get("user_id")
|
||||
author_dict = info.context.get("author", {})
|
||||
author_id = author_dict.get("id")
|
||||
|
||||
if not user_id or not author_id:
|
||||
return {"error": "User ID and author ID are required"}
|
||||
|
||||
with local_session() as session:
|
||||
draft = Draft(created_by=author_id)
|
||||
if shout_id:
|
||||
draft.shout = shout_id
|
||||
session.add(draft)
|
||||
session.commit()
|
||||
return {"draft": draft}
|
||||
|
||||
|
||||
@mutation.field("update_draft")
|
||||
@login_required
|
||||
async def update_draft(_, info, draft_input):
|
||||
user_id = info.context.get("user_id")
|
||||
author_dict = info.context.get("author", {})
|
||||
author_id = author_dict.get("id")
|
||||
draft_id = draft_input.get("id")
|
||||
if not user_id or not author_id:
|
||||
return {"error": "User ID and author ID are required"}
|
||||
|
||||
with local_session() as session:
|
||||
draft = session.query(Draft).filter(Draft.id == draft_id).first()
|
||||
Draft.update(draft, {**draft_input})
|
||||
if not draft:
|
||||
return {"error": "Draft not found"}
|
||||
|
||||
draft.updated_at = int(time.time())
|
||||
session.commit()
|
||||
return {"draft": draft}
|
||||
|
||||
|
||||
@mutation.field("delete_draft")
|
||||
@login_required
|
||||
async def delete_draft(_, info, draft_id: int):
|
||||
user_id = info.context.get("user_id")
|
||||
author_dict = info.context.get("author", {})
|
||||
author_id = author_dict.get("id")
|
||||
|
||||
with local_session() as session:
|
||||
draft = session.query(Draft).filter(Draft.id == draft_id).first()
|
||||
if not draft:
|
||||
return {"error": "Draft not found"}
|
||||
session.delete(draft)
|
||||
session.commit()
|
||||
return {"draft": draft}
|
||||
|
||||
|
||||
@mutation.field("publish_draft")
|
||||
@login_required
|
||||
async def publish_draft(_, info, draft_id: int):
|
||||
user_id = info.context.get("user_id")
|
||||
author_dict = info.context.get("author", {})
|
||||
author_id = author_dict.get("id")
|
||||
if not user_id or not author_id:
|
||||
return {"error": "User ID and author ID are required"}
|
||||
|
||||
with local_session() as session:
|
||||
draft = session.query(Draft).filter(Draft.id == draft_id).first()
|
||||
if not draft:
|
||||
return {"error": "Draft not found"}
|
||||
return publish_shout(None, None, draft.shout, draft)
|
||||
|
||||
|
||||
@mutation.field("unpublish_draft")
|
||||
@login_required
|
||||
async def unpublish_draft(_, info, draft_id: int):
|
||||
user_id = info.context.get("user_id")
|
||||
author_dict = info.context.get("author", {})
|
||||
author_id = author_dict.get("id")
|
||||
if not user_id or not author_id:
|
||||
return {"error": "User ID and author ID are required"}
|
||||
|
||||
with local_session() as session:
|
||||
draft = session.query(Draft).filter(Draft.id == draft_id).first()
|
||||
shout_id = draft.shout
|
||||
unpublish_shout(None, None, shout_id)
|
||||
|
||||
|
||||
@mutation.field("publish_shout")
|
||||
@login_required
|
||||
async def publish_shout(_, info, shout_id: int, draft=None):
|
||||
"""Publish draft as a shout or update existing shout.
|
||||
|
||||
Args:
|
||||
session: SQLAlchemy session to use for database operations
|
||||
"""
|
||||
user_id = info.context.get("user_id")
|
||||
author_dict = info.context.get("author", {})
|
||||
author_id = author_dict.get("id")
|
||||
if not user_id or not author_id:
|
||||
return {"error": "User ID and author ID are required"}
|
||||
|
||||
try:
|
||||
# Use proper SQLAlchemy query
|
||||
with local_session() as session:
|
||||
if not draft:
|
||||
find_draft_stmt = select(Draft).where(Draft.shout == shout_id)
|
||||
draft = session.execute(find_draft_stmt).scalar_one_or_none()
|
||||
|
||||
now = int(time.time())
|
||||
|
||||
if not shout:
|
||||
# Create new shout from draft
|
||||
shout = Shout(
|
||||
body=draft.body,
|
||||
slug=draft.slug,
|
||||
cover=draft.cover,
|
||||
cover_caption=draft.cover_caption,
|
||||
lead=draft.lead,
|
||||
description=draft.description,
|
||||
title=draft.title,
|
||||
subtitle=draft.subtitle,
|
||||
layout=draft.layout,
|
||||
media=draft.media,
|
||||
lang=draft.lang,
|
||||
seo=draft.seo,
|
||||
created_by=author_id,
|
||||
community=draft.community,
|
||||
authors=draft.authors.copy(), # Create copies of relationships
|
||||
topics=draft.topics.copy(),
|
||||
draft=draft.id,
|
||||
deleted_at=None,
|
||||
)
|
||||
else:
|
||||
# Update existing shout
|
||||
shout.authors = draft.authors.copy()
|
||||
shout.topics = draft.topics.copy()
|
||||
shout.draft = draft.id
|
||||
shout.created_by = author_id
|
||||
shout.title = draft.title
|
||||
shout.subtitle = draft.subtitle
|
||||
shout.body = draft.body
|
||||
shout.cover = draft.cover
|
||||
shout.cover_caption = draft.cover_caption
|
||||
shout.lead = draft.lead
|
||||
shout.description = draft.description
|
||||
shout.layout = draft.layout
|
||||
shout.media = draft.media
|
||||
shout.lang = draft.lang
|
||||
shout.seo = draft.seo
|
||||
|
||||
shout.updated_at = now
|
||||
shout.published_at = now
|
||||
draft.updated_at = now
|
||||
draft.published_at = now
|
||||
session.add(shout)
|
||||
session.add(draft)
|
||||
session.commit()
|
||||
|
||||
invalidate_shout_related_cache(shout)
|
||||
invalidate_shouts_cache()
|
||||
return {"shout": shout}
|
||||
except Exception as e:
|
||||
import traceback
|
||||
|
||||
logger.error(f"Failed to publish shout: {e}")
|
||||
logger.error(traceback.format_exc())
|
||||
session.rollback()
|
||||
return {"error": "Failed to publish shout"}
|
||||
|
||||
|
||||
@mutation.field("unpublish_shout")
|
||||
@login_required
|
||||
async def unpublish_shout(_, info, shout_id: int):
|
||||
"""Unpublish a shout.
|
||||
|
||||
Args:
|
||||
shout_id: The ID of the shout to unpublish
|
||||
|
||||
Returns:
|
||||
dict: The unpublished shout or an error message
|
||||
"""
|
||||
author_dict = info.context.get("author", {})
|
||||
author_id = author_dict.get("id")
|
||||
if not author_id:
|
||||
return {"error": "Author ID is required"}
|
||||
|
||||
shout = None
|
||||
with local_session() as session:
|
||||
try:
|
||||
shout = session.query(Shout).filter(Shout.id == shout_id).first()
|
||||
shout.published_at = None
|
||||
session.commit()
|
||||
invalidate_shout_related_cache(shout)
|
||||
invalidate_shouts_cache()
|
||||
|
||||
except Exception:
|
||||
session.rollback()
|
||||
return {"error": "Failed to unpublish shout"}
|
||||
|
||||
return {"shout": shout}
|
|
@ -7,8 +7,10 @@ from sqlalchemy.sql.functions import coalesce
|
|||
|
||||
from cache.cache import cache_author, cache_topic, invalidate_shout_related_cache, invalidate_shouts_cache
|
||||
from orm.author import Author
|
||||
from orm.draft import Draft
|
||||
from orm.shout import Shout, ShoutAuthor, ShoutTopic
|
||||
from orm.topic import Topic
|
||||
from resolvers.draft import create_draft, publish_draft
|
||||
from resolvers.follower import follow, unfollow
|
||||
from resolvers.stat import get_with_stat
|
||||
from services.auth import login_required
|
||||
|
@ -20,6 +22,23 @@ from utils.logger import root_logger as logger
|
|||
|
||||
|
||||
async def cache_by_id(entity, entity_id: int, cache_method):
|
||||
"""Cache an entity by its ID using the provided cache method.
|
||||
|
||||
Args:
|
||||
entity: The SQLAlchemy model class to query
|
||||
entity_id (int): The ID of the entity to cache
|
||||
cache_method: The caching function to use
|
||||
|
||||
Returns:
|
||||
dict: The cached entity data if successful, None if entity not found
|
||||
|
||||
Example:
|
||||
>>> async def test_cache():
|
||||
... author = await cache_by_id(Author, 1, cache_author)
|
||||
... assert author['id'] == 1
|
||||
... assert 'name' in author
|
||||
... return author
|
||||
"""
|
||||
caching_query = select(entity).filter(entity.id == entity_id)
|
||||
result = get_with_stat(caching_query)
|
||||
if not result or not result[0]:
|
||||
|
@ -34,7 +53,34 @@ async def cache_by_id(entity, entity_id: int, cache_method):
|
|||
@query.field("get_my_shout")
|
||||
@login_required
|
||||
async def get_my_shout(_, info, shout_id: int):
|
||||
# logger.debug(info)
|
||||
"""Get a shout by ID if the requesting user has permission to view it.
|
||||
|
||||
DEPRECATED: use `load_drafts` instead
|
||||
|
||||
Args:
|
||||
info: GraphQL resolver info containing context
|
||||
shout_id (int): ID of the shout to retrieve
|
||||
|
||||
Returns:
|
||||
dict: Contains either:
|
||||
- error (str): Error message if retrieval failed
|
||||
- shout (Shout): The requested shout if found and accessible
|
||||
|
||||
Permissions:
|
||||
User must be:
|
||||
- The shout creator
|
||||
- Listed as an author
|
||||
- Have editor role
|
||||
|
||||
Example:
|
||||
>>> async def test_get_my_shout():
|
||||
... context = {'user_id': '123', 'author': {'id': 1}, 'roles': []}
|
||||
... info = type('Info', (), {'context': context})()
|
||||
... result = await get_my_shout(None, info, 1)
|
||||
... assert result['error'] is None
|
||||
... assert result['shout'].id == 1
|
||||
... return result
|
||||
"""
|
||||
user_id = info.context.get("user_id", "")
|
||||
author_dict = info.context.get("author", {})
|
||||
author_id = author_dict.get("id")
|
||||
|
@ -105,8 +151,8 @@ async def get_shouts_drafts(_, info):
|
|||
return {"shouts": shouts}
|
||||
|
||||
|
||||
@mutation.field("create_shout")
|
||||
@login_required
|
||||
# @mutation.field("create_shout")
|
||||
# @login_required
|
||||
async def create_shout(_, info, inp):
|
||||
logger.info(f"Starting create_shout with input: {inp}")
|
||||
user_id = info.context.get("user_id")
|
||||
|
@ -214,6 +260,27 @@ async def create_shout(_, info, inp):
|
|||
|
||||
|
||||
def patch_main_topic(session, main_topic_slug, shout):
|
||||
"""Update the main topic for a shout.
|
||||
|
||||
Args:
|
||||
session: SQLAlchemy session
|
||||
main_topic_slug (str): Slug of the topic to set as main
|
||||
shout (Shout): The shout to update
|
||||
|
||||
Side Effects:
|
||||
- Updates ShoutTopic.main flags in database
|
||||
- Only one topic can be main at a time
|
||||
|
||||
Example:
|
||||
>>> def test_patch_main_topic():
|
||||
... with local_session() as session:
|
||||
... shout = session.query(Shout).first()
|
||||
... patch_main_topic(session, 'tech', shout)
|
||||
... main_topic = session.query(ShoutTopic).filter_by(
|
||||
... shout=shout.id, main=True).first()
|
||||
... assert main_topic.topic.slug == 'tech'
|
||||
... return main_topic
|
||||
"""
|
||||
logger.info(f"Starting patch_main_topic for shout#{shout.id} with slug '{main_topic_slug}'")
|
||||
|
||||
with session.begin():
|
||||
|
@ -252,6 +319,34 @@ def patch_main_topic(session, main_topic_slug, shout):
|
|||
|
||||
|
||||
def patch_topics(session, shout, topics_input):
|
||||
"""Update the topics associated with a shout.
|
||||
|
||||
Args:
|
||||
session: SQLAlchemy session
|
||||
shout (Shout): The shout to update
|
||||
topics_input (list): List of topic dicts with fields:
|
||||
- id (int): Topic ID (<0 for new topics)
|
||||
- slug (str): Topic slug
|
||||
- title (str): Topic title (for new topics)
|
||||
|
||||
Side Effects:
|
||||
- Creates new topics if needed
|
||||
- Updates shout-topic associations
|
||||
- Refreshes shout object with new topics
|
||||
|
||||
Example:
|
||||
>>> def test_patch_topics():
|
||||
... topics = [
|
||||
... {'id': -1, 'slug': 'new-topic', 'title': 'New Topic'},
|
||||
... {'id': 1, 'slug': 'existing-topic'}
|
||||
... ]
|
||||
... with local_session() as session:
|
||||
... shout = session.query(Shout).first()
|
||||
... patch_topics(session, shout, topics)
|
||||
... assert len(shout.topics) == 2
|
||||
... assert any(t.slug == 'new-topic' for t in shout.topics)
|
||||
... return shout.topics
|
||||
"""
|
||||
logger.info(f"Starting patch_topics for shout#{shout.id}")
|
||||
logger.info(f"Received topics_input: {topics_input}")
|
||||
|
||||
|
@ -292,8 +387,8 @@ def patch_topics(session, shout, topics_input):
|
|||
logger.info(f"Final shout topics: {[t.dict() for t in shout.topics]}")
|
||||
|
||||
|
||||
@mutation.field("update_shout")
|
||||
@login_required
|
||||
# @mutation.field("update_shout")
|
||||
# @login_required
|
||||
async def update_shout(_, info, shout_id: int, shout_input=None, publish=False):
|
||||
logger.info(f"Starting update_shout with id={shout_id}, publish={publish}")
|
||||
logger.debug(f"Full shout_input: {shout_input}")
|
||||
|
@ -505,8 +600,8 @@ async def update_shout(_, info, shout_id: int, shout_input=None, publish=False):
|
|||
return {"error": "cant update shout"}
|
||||
|
||||
|
||||
@mutation.field("delete_shout")
|
||||
@login_required
|
||||
# @mutation.field("delete_shout")
|
||||
# @login_required
|
||||
async def delete_shout(_, info, shout_id: int):
|
||||
user_id = info.context.get("user_id")
|
||||
roles = info.context.get("roles", [])
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
input ShoutInput {
|
||||
input DraftInput {
|
||||
slug: String
|
||||
title: String
|
||||
body: String
|
||||
|
|
|
@ -3,10 +3,15 @@ type Mutation {
|
|||
rate_author(rated_slug: String!, value: Int!): CommonResult!
|
||||
update_author(profile: ProfileInput!): CommonResult!
|
||||
|
||||
# editor
|
||||
create_shout(inp: ShoutInput!): CommonResult!
|
||||
update_shout(shout_id: Int!, shout_input: ShoutInput, publish: Boolean): CommonResult!
|
||||
delete_shout(shout_id: Int!): CommonResult!
|
||||
# draft
|
||||
create_draft(input: DraftInput!): CommonResult!
|
||||
update_draft(draft_id: Int!, input: DraftInput!): CommonResult!
|
||||
delete_draft(draft_id: Int!): CommonResult!
|
||||
# publication
|
||||
publish_shout(shout_id: Int!): CommonResult!
|
||||
publish_draft(draft_id: Int!): CommonResult!
|
||||
unpublish_draft(draft_id: Int!): CommonResult!
|
||||
unpublish_shout(shout_id: Int!): CommonResult!
|
||||
|
||||
# follower
|
||||
follow(what: FollowingEntity!, slug: String!): AuthorFollowsResult!
|
||||
|
|
|
@ -51,6 +51,7 @@ type Query {
|
|||
# editor
|
||||
get_my_shout(shout_id: Int!): CommonResult!
|
||||
get_shouts_drafts: CommonResult!
|
||||
load_drafts: CommonResult!
|
||||
|
||||
# topic
|
||||
get_topic(slug: String!): Topic
|
||||
|
|
|
@ -100,12 +100,37 @@ type Shout {
|
|||
deleted_at: Int
|
||||
|
||||
version_of: Shout # TODO: use version_of somewhere
|
||||
|
||||
draft: Draft
|
||||
media: [MediaItem]
|
||||
stat: Stat
|
||||
score: Float
|
||||
}
|
||||
|
||||
type Draft {
|
||||
id: Int!
|
||||
shout: Shout
|
||||
created_at: Int!
|
||||
updated_at: Int
|
||||
deleted_at: Int
|
||||
created_by: Author!
|
||||
updated_by: Author
|
||||
deleted_by: Author
|
||||
authors: [Author]
|
||||
topics: [Topic]
|
||||
media: [MediaItem]
|
||||
lead: String
|
||||
description: String
|
||||
subtitle: String
|
||||
layout: String
|
||||
lang: String
|
||||
seo: String
|
||||
body: String
|
||||
title: String
|
||||
slug: String
|
||||
cover: String
|
||||
cover_caption: String
|
||||
}
|
||||
|
||||
type Stat {
|
||||
rating: Int
|
||||
commented: Int
|
||||
|
|
Loading…
Reference in New Issue
Block a user