Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions migrations/20250929000000_init_comments.sql
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
-- +goose Up
-- +goose StatementBegin
CREATE TYPE comment_status AS ENUM ('DELETED', 'HIDDEN', 'ON_MODERATION');

CREATE TABLE IF NOT EXISTS comments (
id BIGSERIAL PRIMARY KEY,
mod_id BIGINT NOT NULL,
author_id BIGINT NOT NULL,
text TEXT NOT NULL,
status comment_status,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
edited_at TIMESTAMPTZ
);
Expand All @@ -15,4 +18,5 @@ CREATE INDEX IF NOT EXISTS idx_comments_mod_id ON comments (mod_id);
-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS comments;
DROP TYPE IF EXISTS comment_status;
-- +goose StatementEnd
3 changes: 3 additions & 0 deletions src/commentservice/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
STATUS_DELETED = "DELETED"
STATUS_HIDDEN = "HIDDEN"
STATUS_ON_MODERATION = "ON_MODERATION"
24 changes: 13 additions & 11 deletions src/commentservice/grpc/comment_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 18 additions & 4 deletions src/commentservice/grpc/comment_pb2.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,25 @@ import datetime

from google.protobuf import timestamp_pb2 as _timestamp_pb2
from google.protobuf.internal import containers as _containers
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from collections.abc import Iterable as _Iterable, Mapping as _Mapping
from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union

DESCRIPTOR: _descriptor.FileDescriptor

class CommentStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
__slots__ = ()
COMMENT_STATUS_UNSPECIFIED: _ClassVar[CommentStatus]
COMMENT_STATUS_DELETED: _ClassVar[CommentStatus]
COMMENT_STATUS_HIDDEN: _ClassVar[CommentStatus]
COMMENT_STATUS_ON_MODERATION: _ClassVar[CommentStatus]
COMMENT_STATUS_UNSPECIFIED: CommentStatus
COMMENT_STATUS_DELETED: CommentStatus
COMMENT_STATUS_HIDDEN: CommentStatus
COMMENT_STATUS_ON_MODERATION: CommentStatus

class Comment(_message.Message):
__slots__ = ("id", "author_id", "text", "created_at", "edited_at")
ID_FIELD_NUMBER: _ClassVar[int]
Expand Down Expand Up @@ -53,13 +65,15 @@ class GetCommentsResponse(_message.Message):
comments: _containers.RepeatedCompositeFieldContainer[Comment]
def __init__(self, mod_id: _Optional[int] = ..., comments: _Optional[_Iterable[_Union[Comment, _Mapping]]] = ...) -> None: ...

class DeleteCommentRequest(_message.Message):
__slots__ = ("comment_id",)
class SetStatusRequest(_message.Message):
__slots__ = ("comment_id", "status")
COMMENT_ID_FIELD_NUMBER: _ClassVar[int]
STATUS_FIELD_NUMBER: _ClassVar[int]
comment_id: int
def __init__(self, comment_id: _Optional[int] = ...) -> None: ...
status: CommentStatus
def __init__(self, comment_id: _Optional[int] = ..., status: _Optional[_Union[CommentStatus, str]] = ...) -> None: ...

class DeleteCommentResponse(_message.Message):
class SetStatusResponse(_message.Message):
__slots__ = ("success",)
SUCCESS_FIELD_NUMBER: _ClassVar[int]
success: bool
Expand Down
47 changes: 27 additions & 20 deletions src/commentservice/grpc/comment_pb2_grpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@


class CommentServiceStub(object):
"""Missing associated documentation comment in .proto file."""
"""Сервис для работы с комментариями: создание, получение, изменение статуса, редактирование
"""

def __init__(self, channel):
"""Constructor.
Expand All @@ -44,10 +45,10 @@ def __init__(self, channel):
request_serializer=comment__pb2.GetCommentsRequest.SerializeToString,
response_deserializer=comment__pb2.GetCommentsResponse.FromString,
_registered_method=True)
self.DeleteComment = channel.unary_unary(
'/comment.CommentService/DeleteComment',
request_serializer=comment__pb2.DeleteCommentRequest.SerializeToString,
response_deserializer=comment__pb2.DeleteCommentResponse.FromString,
self.SetStatus = channel.unary_unary(
'/comment.CommentService/SetStatus',
request_serializer=comment__pb2.SetStatusRequest.SerializeToString,
response_deserializer=comment__pb2.SetStatusResponse.FromString,
_registered_method=True)
self.EditComment = channel.unary_unary(
'/comment.CommentService/EditComment',
Expand All @@ -57,28 +58,33 @@ def __init__(self, channel):


class CommentServiceServicer(object):
"""Missing associated documentation comment in .proto file."""
"""Сервис для работы с комментариями: создание, получение, изменение статуса, редактирование
"""

def CreateComment(self, request, context):
"""Missing associated documentation comment in .proto file."""
"""Создание комментария
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')

def GetComments(self, request, context):
"""Missing associated documentation comment in .proto file."""
"""Получение комментариев
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')

def DeleteComment(self, request, context):
"""Missing associated documentation comment in .proto file."""
def SetStatus(self, request, context):
"""Изменение статуса комментария
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')

def EditComment(self, request, context):
"""Missing associated documentation comment in .proto file."""
"""Редактирование комментария
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
Expand All @@ -96,10 +102,10 @@ def add_CommentServiceServicer_to_server(servicer, server):
request_deserializer=comment__pb2.GetCommentsRequest.FromString,
response_serializer=comment__pb2.GetCommentsResponse.SerializeToString,
),
'DeleteComment': grpc.unary_unary_rpc_method_handler(
servicer.DeleteComment,
request_deserializer=comment__pb2.DeleteCommentRequest.FromString,
response_serializer=comment__pb2.DeleteCommentResponse.SerializeToString,
'SetStatus': grpc.unary_unary_rpc_method_handler(
servicer.SetStatus,
request_deserializer=comment__pb2.SetStatusRequest.FromString,
response_serializer=comment__pb2.SetStatusResponse.SerializeToString,
),
'EditComment': grpc.unary_unary_rpc_method_handler(
servicer.EditComment,
Expand All @@ -115,7 +121,8 @@ def add_CommentServiceServicer_to_server(servicer, server):

# This class is part of an EXPERIMENTAL API.
class CommentService(object):
"""Missing associated documentation comment in .proto file."""
"""Сервис для работы с комментариями: создание, получение, изменение статуса, редактирование
"""

@staticmethod
def CreateComment(request,
Expand Down Expand Up @@ -172,7 +179,7 @@ def GetComments(request,
_registered_method=True)

@staticmethod
def DeleteComment(request,
def SetStatus(request,
target,
options=(),
channel_credentials=None,
Expand All @@ -185,9 +192,9 @@ def DeleteComment(request,
return grpc.experimental.unary_unary(
request,
target,
'/comment.CommentService/DeleteComment',
comment__pb2.DeleteCommentRequest.SerializeToString,
comment__pb2.DeleteCommentResponse.FromString,
'/comment.CommentService/SetStatus',
comment__pb2.SetStatusRequest.SerializeToString,
comment__pb2.SetStatusResponse.FromString,
options,
channel_credentials,
insecure,
Expand Down
13 changes: 0 additions & 13 deletions src/commentservice/handler/delete_comment.py

This file was deleted.

12 changes: 5 additions & 7 deletions src/commentservice/handler/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@
from commentservice.handler.create_comment import (
CreateComment as _create_comment,
)
from commentservice.handler.delete_comment import (
DeleteComment as _delete_comment,
)
from commentservice.handler.edit_comment import EditComment as _edit_comment
from commentservice.handler.get_comments import GetComments as _get_comments
from commentservice.handler.set_status import SetStatus as _set_status
from commentservice.service.service import CommentService


Expand All @@ -30,12 +28,12 @@ async def EditComment(
) -> comment_pb2.EditCommentResponse:
return await _edit_comment(self._service, request, context)

async def DeleteComment(
async def SetStatus(
self,
request: comment_pb2.DeleteCommentRequest,
request: comment_pb2.SetStatusRequest,
context: grpc.ServicerContext,
) -> comment_pb2.DeleteCommentResponse:
return await _delete_comment(self._service, request, context)
) -> comment_pb2.SetStatusResponse:
return await _set_status(self._service, request, context)

async def GetComments(
self,
Expand Down
41 changes: 41 additions & 0 deletions src/commentservice/handler/set_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import grpc

from commentservice.constants import (
STATUS_DELETED,
STATUS_HIDDEN,
STATUS_ON_MODERATION,
)
from commentservice.grpc import comment_pb2
from commentservice.grpc.comment_pb2 import CommentStatus
from commentservice.service.service import CommentService

_ENUM_TO_DB_STATUS_BY_VALUE: dict[int, str] = {
CommentStatus.COMMENT_STATUS_DELETED: STATUS_DELETED,
CommentStatus.COMMENT_STATUS_HIDDEN: STATUS_HIDDEN,
CommentStatus.COMMENT_STATUS_ON_MODERATION: STATUS_ON_MODERATION,
}


def _convert_enum_to_status(status_value: int) -> str:
if status_value == CommentStatus.COMMENT_STATUS_UNSPECIFIED:
raise ValueError("Status must be specified")
return _ENUM_TO_DB_STATUS_BY_VALUE[status_value]


async def SetStatus(

Check warning on line 25 in src/commentservice/handler/set_status.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename function "SetStatus" to match the regular expression ^[a-z_][a-z0-9_]*$.

See more on https://sonarcloud.io/project/issues?id=esclient_comment-service&issues=AZq3mo0G_1mZfFUECfnM&open=AZq3mo0G_1mZfFUECfnM&pullRequest=35
service: CommentService,
request: comment_pb2.SetStatusRequest,
context: grpc.ServicerContext, # noqa: ARG001
) -> comment_pb2.SetStatusResponse:
try:
status_str = _convert_enum_to_status(request.status)
success = await service.set_status(request.comment_id, status_str)
return comment_pb2.SetStatusResponse(success=success)
except ValueError as e:
context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
context.set_details(str(e))
return comment_pb2.SetStatusResponse(success=False)
except Exception as e:
context.set_code(grpc.StatusCode.INTERNAL)
context.set_details(f"Failed to set status: {e!s}")
return comment_pb2.SetStatusResponse(success=False)
15 changes: 0 additions & 15 deletions src/commentservice/repository/delete_comment.py

This file was deleted.

8 changes: 3 additions & 5 deletions src/commentservice/repository/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@
from commentservice.repository.create_comment import (
create_comment as _create_comment,
)
from commentservice.repository.delete_comment import (
delete_comment as _delete_comment,
)
from commentservice.repository.edit_comment import (
edit_comment as _edit_comment,
)
from commentservice.repository.get_comments import (
get_comments as _get_comments,
)
from commentservice.repository.model import Comment
from commentservice.repository.set_status import set_status as _set_status


class CommentRepository:
Expand All @@ -30,8 +28,8 @@ async def create_comment(
async def edit_comment(self, comment_id: int, new_text: str) -> bool:
return await _edit_comment(self._db_pool, comment_id, new_text)

async def delete_comment(self, comment_id: int) -> bool:
return await _delete_comment(self._db_pool, comment_id)
async def set_status(self, comment_id: int, status: str) -> bool:
return await _set_status(self._db_pool, comment_id, status)

async def get_comments(self, mod_id: int) -> list[Comment]:
return await _get_comments(self._db_pool, mod_id)
19 changes: 19 additions & 0 deletions src/commentservice/repository/set_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from asyncpg import Pool


async def set_status(db_pool: Pool, comment_id: int, status: str) -> bool:
try:
async with db_pool.acquire() as conn:
result = await conn.execute(
"""
UPDATE comments
SET status = $1
WHERE id = $2
""",
status,
comment_id,
)
rows_affected = int(result.split()[-1]) if result else 0
return rows_affected > 0
except Exception:
return False
3 changes: 2 additions & 1 deletion src/commentservice/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ async def serve() -> None:

server = grpc.aio.server(futures.ThreadPoolExecutor(max_workers=5))
comment_pb2_grpc.add_CommentServiceServicer_to_server(
handler, server
handler,
server,
) # type: ignore[no-untyped-call]

SERVICE_NAMES = (
Expand Down
5 changes: 0 additions & 5 deletions src/commentservice/service/delete_comment.py

This file was deleted.

Loading