diff --git a/justfile b/justfile index 4c567c8..01122db 100644 --- a/justfile +++ b/justfile @@ -3,7 +3,7 @@ set dotenv-load := true COMMON_URL := 'https://raw.githubusercontent.com/esclient/tools/refs/heads/main/python/common.just' -PROTO_TAG := 'v0.1.1' +PROTO_TAG := 'v0.1.2' PROTO_NAME := 'mod.proto' TMP_DIR := '.proto' OUT_DIR := 'src/modservice/grpc' diff --git a/src/modservice/grpc/mod_pb2.py b/src/modservice/grpc/mod_pb2.py index 7c0415c..bd7edd2 100644 --- a/src/modservice/grpc/mod_pb2.py +++ b/src/modservice/grpc/mod_pb2.py @@ -22,29 +22,36 @@ _sym_db = _symbol_database.Default() +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tmod.proto\x12\x03mod\"B\n\x10SetStatusRequest\x12\x0e\n\x06mod_id\x18\x01 \x01(\x03\x12\x1e\n\x06status\x18\x02 \x01(\x0e\x32\x0e.mod.ModStatus\"$\n\x11SetStatusResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\"_\n\x10\x43reateModRequest\x12\x11\n\tmod_title\x18\x01 \x01(\t\x12\x11\n\tauthor_id\x18\x02 \x01(\x03\x12\x10\n\x08\x66ilename\x18\x03 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x04 \x01(\t\"G\n\x11\x43reateModResponse\x12\x0e\n\x06mod_id\x18\x01 \x01(\x03\x12\x12\n\nupload_url\x18\x02 \x01(\t\x12\x0e\n\x06s3_key\x18\x03 \x01(\t\"+\n\x19GetModDownloadLinkRequest\x12\x0e\n\x06mod_id\x18\x01 \x01(\x03\".\n\x1aGetModDownloadLinkResponse\x12\x10\n\x08link_url\x18\x01 \x01(\t*n\n\tModStatus\x12\x1a\n\x16MOD_STATUS_UNSPECIFIED\x10\x00\x12\x17\n\x13MOD_STATUS_UPLOADED\x10\x01\x12\x15\n\x11MOD_STATUS_BANNED\x10\x02\x12\x15\n\x11MOD_STATUS_HIDDEN\x10\x03\x32\xdb\x01\n\nModService\x12:\n\tCreateMod\x12\x15.mod.CreateModRequest\x1a\x16.mod.CreateModResponse\x12:\n\tSetStatus\x12\x15.mod.SetStatusRequest\x1a\x16.mod.SetStatusResponse\x12U\n\x12GetModDownloadLink\x12\x1e.mod.GetModDownloadLinkRequest\x1a\x1f.mod.GetModDownloadLinkResponseb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tmod.proto\x12\x03mod\x1a\x1fgoogle/protobuf/timestamp.proto\"\x10\n\x0eGetModsRequest\")\n\x0fGetModsResponse\x12\x16\n\x04mods\x18\x01 \x03(\x0b\x32\x08.mod.Mod\"\xa9\x01\n\x03Mod\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x11\n\tauthor_id\x18\x02 \x01(\x03\x12\r\n\x05title\x18\x03 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x04 \x01(\t\x12\x0f\n\x07version\x18\x05 \x01(\x03\x12\x1e\n\x06status\x18\x06 \x01(\x0e\x32\x0e.mod.ModStatus\x12.\n\ncreated_at\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"B\n\x10SetStatusRequest\x12\x0e\n\x06mod_id\x18\x01 \x01(\x03\x12\x1e\n\x06status\x18\x02 \x01(\x0e\x32\x0e.mod.ModStatus\"$\n\x11SetStatusResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\"[\n\x10\x43reateModRequest\x12\r\n\x05title\x18\x01 \x01(\t\x12\x11\n\tauthor_id\x18\x02 \x01(\x03\x12\x10\n\x08\x66ilename\x18\x03 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x04 \x01(\t\"G\n\x11\x43reateModResponse\x12\x0e\n\x06mod_id\x18\x01 \x01(\x03\x12\x12\n\nupload_url\x18\x02 \x01(\t\x12\x0e\n\x06s3_key\x18\x03 \x01(\t\"+\n\x19GetModDownloadLinkRequest\x12\x0e\n\x06mod_id\x18\x01 \x01(\x03\".\n\x1aGetModDownloadLinkResponse\x12\x10\n\x08link_url\x18\x01 \x01(\t*n\n\tModStatus\x12\x1a\n\x16MOD_STATUS_UNSPECIFIED\x10\x00\x12\x17\n\x13MOD_STATUS_UPLOADED\x10\x01\x12\x15\n\x11MOD_STATUS_BANNED\x10\x02\x12\x15\n\x11MOD_STATUS_HIDDEN\x10\x03\x32\x91\x02\n\nModService\x12:\n\tCreateMod\x12\x15.mod.CreateModRequest\x1a\x16.mod.CreateModResponse\x12:\n\tSetStatus\x12\x15.mod.SetStatusRequest\x1a\x16.mod.SetStatusResponse\x12U\n\x12GetModDownloadLink\x12\x1e.mod.GetModDownloadLinkRequest\x1a\x1f.mod.GetModDownloadLinkResponse\x12\x34\n\x07GetMods\x12\x13.mod.GetModsRequest\x1a\x14.mod.GetModsResponseb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'mod_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: DESCRIPTOR._loaded_options = None - _globals['_MODSTATUS']._serialized_start=387 - _globals['_MODSTATUS']._serialized_end=497 - _globals['_SETSTATUSREQUEST']._serialized_start=18 - _globals['_SETSTATUSREQUEST']._serialized_end=84 - _globals['_SETSTATUSRESPONSE']._serialized_start=86 - _globals['_SETSTATUSRESPONSE']._serialized_end=122 - _globals['_CREATEMODREQUEST']._serialized_start=124 - _globals['_CREATEMODREQUEST']._serialized_end=219 - _globals['_CREATEMODRESPONSE']._serialized_start=221 - _globals['_CREATEMODRESPONSE']._serialized_end=292 - _globals['_GETMODDOWNLOADLINKREQUEST']._serialized_start=294 - _globals['_GETMODDOWNLOADLINKREQUEST']._serialized_end=337 - _globals['_GETMODDOWNLOADLINKRESPONSE']._serialized_start=339 - _globals['_GETMODDOWNLOADLINKRESPONSE']._serialized_end=385 - _globals['_MODSERVICE']._serialized_start=500 - _globals['_MODSERVICE']._serialized_end=719 + _globals['_MODSTATUS']._serialized_start=649 + _globals['_MODSTATUS']._serialized_end=759 + _globals['_GETMODSREQUEST']._serialized_start=51 + _globals['_GETMODSREQUEST']._serialized_end=67 + _globals['_GETMODSRESPONSE']._serialized_start=69 + _globals['_GETMODSRESPONSE']._serialized_end=110 + _globals['_MOD']._serialized_start=113 + _globals['_MOD']._serialized_end=282 + _globals['_SETSTATUSREQUEST']._serialized_start=284 + _globals['_SETSTATUSREQUEST']._serialized_end=350 + _globals['_SETSTATUSRESPONSE']._serialized_start=352 + _globals['_SETSTATUSRESPONSE']._serialized_end=388 + _globals['_CREATEMODREQUEST']._serialized_start=390 + _globals['_CREATEMODREQUEST']._serialized_end=481 + _globals['_CREATEMODRESPONSE']._serialized_start=483 + _globals['_CREATEMODRESPONSE']._serialized_end=554 + _globals['_GETMODDOWNLOADLINKREQUEST']._serialized_start=556 + _globals['_GETMODDOWNLOADLINKREQUEST']._serialized_end=599 + _globals['_GETMODDOWNLOADLINKRESPONSE']._serialized_start=601 + _globals['_GETMODDOWNLOADLINKRESPONSE']._serialized_end=647 + _globals['_MODSERVICE']._serialized_start=762 + _globals['_MODSERVICE']._serialized_end=1035 # @@protoc_insertion_point(module_scope) diff --git a/src/modservice/grpc/mod_pb2.pyi b/src/modservice/grpc/mod_pb2.pyi index 0580a95..40a1cb7 100644 --- a/src/modservice/grpc/mod_pb2.pyi +++ b/src/modservice/grpc/mod_pb2.pyi @@ -1,6 +1,11 @@ +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 @@ -16,6 +21,34 @@ MOD_STATUS_UPLOADED: ModStatus MOD_STATUS_BANNED: ModStatus MOD_STATUS_HIDDEN: ModStatus +class GetModsRequest(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class GetModsResponse(_message.Message): + __slots__ = ("mods",) + MODS_FIELD_NUMBER: _ClassVar[int] + mods: _containers.RepeatedCompositeFieldContainer[Mod] + def __init__(self, mods: _Optional[_Iterable[_Union[Mod, _Mapping]]] = ...) -> None: ... + +class Mod(_message.Message): + __slots__ = ("id", "author_id", "title", "description", "version", "status", "created_at") + ID_FIELD_NUMBER: _ClassVar[int] + AUTHOR_ID_FIELD_NUMBER: _ClassVar[int] + TITLE_FIELD_NUMBER: _ClassVar[int] + DESCRIPTION_FIELD_NUMBER: _ClassVar[int] + VERSION_FIELD_NUMBER: _ClassVar[int] + STATUS_FIELD_NUMBER: _ClassVar[int] + CREATED_AT_FIELD_NUMBER: _ClassVar[int] + id: int + author_id: int + title: str + description: str + version: int + status: ModStatus + created_at: _timestamp_pb2.Timestamp + def __init__(self, id: _Optional[int] = ..., author_id: _Optional[int] = ..., title: _Optional[str] = ..., description: _Optional[str] = ..., version: _Optional[int] = ..., status: _Optional[_Union[ModStatus, str]] = ..., created_at: _Optional[_Union[datetime.datetime, _timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ... + class SetStatusRequest(_message.Message): __slots__ = ("mod_id", "status") MOD_ID_FIELD_NUMBER: _ClassVar[int] @@ -31,16 +64,16 @@ class SetStatusResponse(_message.Message): def __init__(self, success: bool = ...) -> None: ... class CreateModRequest(_message.Message): - __slots__ = ("mod_title", "author_id", "filename", "description") - MOD_TITLE_FIELD_NUMBER: _ClassVar[int] + __slots__ = ("title", "author_id", "filename", "description") + TITLE_FIELD_NUMBER: _ClassVar[int] AUTHOR_ID_FIELD_NUMBER: _ClassVar[int] FILENAME_FIELD_NUMBER: _ClassVar[int] DESCRIPTION_FIELD_NUMBER: _ClassVar[int] - mod_title: str + title: str author_id: int filename: str description: str - def __init__(self, mod_title: _Optional[str] = ..., author_id: _Optional[int] = ..., filename: _Optional[str] = ..., description: _Optional[str] = ...) -> None: ... + def __init__(self, title: _Optional[str] = ..., author_id: _Optional[int] = ..., filename: _Optional[str] = ..., description: _Optional[str] = ...) -> None: ... class CreateModResponse(_message.Message): __slots__ = ("mod_id", "upload_url", "s3_key") diff --git a/src/modservice/grpc/mod_pb2_grpc.py b/src/modservice/grpc/mod_pb2_grpc.py index 7aefcbc..008688a 100644 --- a/src/modservice/grpc/mod_pb2_grpc.py +++ b/src/modservice/grpc/mod_pb2_grpc.py @@ -49,6 +49,11 @@ def __init__(self, channel): request_serializer=mod__pb2.GetModDownloadLinkRequest.SerializeToString, response_deserializer=mod__pb2.GetModDownloadLinkResponse.FromString, _registered_method=True) + self.GetMods = channel.unary_unary( + '/mod.ModService/GetMods', + request_serializer=mod__pb2.GetModsRequest.SerializeToString, + response_deserializer=mod__pb2.GetModsResponse.FromString, + _registered_method=True) class ModServiceServicer(object): @@ -72,6 +77,12 @@ def GetModDownloadLink(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def GetMods(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 add_ModServiceServicer_to_server(servicer, server): rpc_method_handlers = { @@ -90,6 +101,11 @@ def add_ModServiceServicer_to_server(servicer, server): request_deserializer=mod__pb2.GetModDownloadLinkRequest.FromString, response_serializer=mod__pb2.GetModDownloadLinkResponse.SerializeToString, ), + 'GetMods': grpc.unary_unary_rpc_method_handler( + servicer.GetMods, + request_deserializer=mod__pb2.GetModsRequest.FromString, + response_serializer=mod__pb2.GetModsResponse.SerializeToString, + ), } generic_handler = grpc.method_handlers_generic_handler( 'mod.ModService', rpc_method_handlers) @@ -181,3 +197,30 @@ def GetModDownloadLink(request, timeout, metadata, _registered_method=True) + + @staticmethod + def GetMods(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/mod.ModService/GetMods', + mod__pb2.GetModsRequest.SerializeToString, + mod__pb2.GetModsResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/src/modservice/handler/create_mod.py b/src/modservice/handler/create_mod.py index 2598916..323353d 100644 --- a/src/modservice/handler/create_mod.py +++ b/src/modservice/handler/create_mod.py @@ -11,7 +11,7 @@ def CreateMod( ) -> mod_pb2.CreateModResponse: # Создаем мод mod_id, s3_key, upload_url = service.create_mod( - request.mod_title, + request.title, request.author_id, request.description, ) diff --git a/src/modservice/handler/get_mods.py b/src/modservice/handler/get_mods.py new file mode 100644 index 0000000..5ca521f --- /dev/null +++ b/src/modservice/handler/get_mods.py @@ -0,0 +1,35 @@ +import grpc + +from modservice.grpc import mod_pb2 +from modservice.service.service import ModService + + +def GetMods( + service: ModService, + request: mod_pb2.GetModsRequest, # noqa: ARG001 + context: grpc.ServicerContext, # noqa: ARG001 +) -> mod_pb2.GetModsResponse: + mods_data = service.get_mods() + + mods = [] + for mod_data in mods_data: + mod = mod_pb2.Mod( + id=mod_data["id"], + author_id=mod_data["author_id"], + title=mod_data["title"], + description=mod_data["description"], + version=mod_data["version"], + status=mod_data["status"], + created_at=mod_data["created_at"] + if mod_data.get("created_at") + else None, + # TODO: Раскомментировать когда добавятся в БД и proto: + # avatar_url=mod_data.get("avatar_url", ""), + # download_count=mod_data.get("download_count", 0), + # rating=mod_data.get("rating", 0.0), + # tags=mod_data.get("tags", []), + # updated_at=mod_data["updated_at"] if mod_data.get("updated_at") else None, + ) + mods.append(mod) + + return mod_pb2.GetModsResponse(mods=mods) diff --git a/src/modservice/handler/handler.py b/src/modservice/handler/handler.py index 0fb4edd..a1093c6 100644 --- a/src/modservice/handler/handler.py +++ b/src/modservice/handler/handler.py @@ -5,6 +5,7 @@ from modservice.handler.get_mod_download_link import ( GetDownloadLink as _get_mod_download_link, ) +from modservice.handler.get_mods import GetMods as _get_mods from modservice.handler.set_status import SetStatus as _set_status from modservice.service.service import ModService @@ -33,3 +34,10 @@ def SetStatus( context: grpc.ServicerContext, ) -> mod_pb2.SetStatusResponse: return _set_status(self._service, request, context) + + def GetMods( + self, + request: mod_pb2.GetModsRequest, + context: grpc.ServicerContext, + ) -> mod_pb2.GetModsResponse: + return _get_mods(self._service, request, context) diff --git a/src/modservice/repository/create_mod.py b/src/modservice/repository/create_mod.py index a8f8c49..c551ea5 100644 --- a/src/modservice/repository/create_mod.py +++ b/src/modservice/repository/create_mod.py @@ -3,7 +3,7 @@ def create_mod( db_pool: ThreadedConnectionPool, - mod_title: str, # noqa: ARG001 + title: str, # noqa: ARG001 author_id: int, # noqa: ARG001 description: str, # noqa: ARG001 ) -> int: @@ -18,7 +18,7 @@ def create_mod( """, ( author_id, - mod_title, + title, description, 1, ), diff --git a/src/modservice/repository/get_mods.py b/src/modservice/repository/get_mods.py new file mode 100644 index 0000000..96722c6 --- /dev/null +++ b/src/modservice/repository/get_mods.py @@ -0,0 +1,52 @@ +from typing import Any + +from psycopg2.pool import ThreadedConnectionPool + + +def get_mods( + db_pool: ThreadedConnectionPool, +) -> list[dict[str, Any]]: + conn = db_pool.getconn() + try: + with conn.cursor() as cursor: + # NOTE: Поля для будущего добавления в БД: + # - avatar_url + # - download_count + # - rating + # - tags + # - updated_at + cursor.execute( + """ + SELECT + id, + author_id, + title, + description, + version, + s3_key, + status, + created_at + FROM mods + ORDER BY created_at DESC + """ + ) + + results = cursor.fetchall() + + mods = [] + for row in results: + mod = { + "id": row[0], + "author_id": row[1], + "title": row[2], + "description": row[3], + "version": row[4], + "s3_key": row[5], + "status": row[6], + "created_at": row[7], + } + mods.append(mod) + + return mods + finally: + db_pool.putconn(conn) diff --git a/src/modservice/repository/model.py b/src/modservice/repository/model.py index 859cf86..1cb555d 100644 --- a/src/modservice/repository/model.py +++ b/src/modservice/repository/model.py @@ -3,7 +3,7 @@ @dataclass class Mod: - mod_title: str + title: str author_id: int filename: str description: str diff --git a/src/modservice/repository/repository.py b/src/modservice/repository/repository.py index 2cdf449..4ae4773 100644 --- a/src/modservice/repository/repository.py +++ b/src/modservice/repository/repository.py @@ -1,9 +1,12 @@ +from typing import Any + from psycopg2.pool import ThreadedConnectionPool from modservice.repository.create_mod import create_mod as _create_mod from modservice.repository.get_mod_s3_key import ( get_mod_s3_key as _get_mod_s3_key, ) +from modservice.repository.get_mods import get_mods as _get_mods from modservice.repository.insert_s3_key import insert_s3_key as _insert_s3_key from modservice.repository.set_status import set_status as _set_status @@ -14,11 +17,11 @@ def __init__(self, db_pool: ThreadedConnectionPool): def create_mod( self, - mod_title: str, + title: str, author_id: int, description: str, ) -> int: - return _create_mod(self._db_pool, mod_title, author_id, description) + return _create_mod(self._db_pool, title, author_id, description) def insert_s3_key( self, @@ -32,3 +35,8 @@ def get_mod_s3_key(self, mod_id: int) -> str: def set_status(self, mod_id: int, status: str) -> bool: return _set_status(self._db_pool, mod_id, status) + + def get_mods( + self, + ) -> list[dict[str, Any]]: + return _get_mods(self._db_pool) diff --git a/src/modservice/service/create_mod.py b/src/modservice/service/create_mod.py index aa88bdf..f09977a 100644 --- a/src/modservice/service/create_mod.py +++ b/src/modservice/service/create_mod.py @@ -5,11 +5,11 @@ def create_mod( repo: ModRepository, s3_service: S3Service, - mod_title: str, + title: str, author_id: int, description: str, ) -> tuple[int, str, str]: - mod_id = repo.create_mod(mod_title, author_id, description) + mod_id = repo.create_mod(title, author_id, description) s3_key = repo.insert_s3_key(mod_id, author_id) diff --git a/src/modservice/service/get_mods.py b/src/modservice/service/get_mods.py new file mode 100644 index 0000000..b6264d3 --- /dev/null +++ b/src/modservice/service/get_mods.py @@ -0,0 +1,9 @@ +from typing import Any + +from modservice.repository.repository import ModRepository + + +def get_mods( + repo: ModRepository, +) -> list[dict[str, Any]]: + return repo.get_mods() diff --git a/src/modservice/service/s3_service.py b/src/modservice/service/s3_service.py index 4c36b3d..330e387 100644 --- a/src/modservice/service/s3_service.py +++ b/src/modservice/service/s3_service.py @@ -14,14 +14,14 @@ def __init__(self, s3_client: S3Client) -> None: self._s3_client = s3_client def generate_s3_key( - self, author_id: int, filename: str, mod_title: str | None = None + self, author_id: int, filename: str, title: str | None = None ) -> str: timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") safe_filename = self._sanitize_filename(filename) - if mod_title: - safe_title = self._sanitize_title(mod_title) + if title: + safe_title = self._sanitize_title(title) file_ext = os.path.splitext(safe_filename)[1] safe_filename = safe_title + file_ext @@ -85,7 +85,7 @@ def generate_upload_url( self, author_id: int, filename: str, - mod_title: str | None = None, + title: str | None = None, expiration: int = 3600, content_type: str | None = None, ) -> tuple[str, str]: @@ -93,7 +93,7 @@ def generate_upload_url( f"Генерируем Presigned PUT URL для автора {author_id}, файл: {filename}" ) - s3_key = self.generate_s3_key(author_id, filename, mod_title) + s3_key = self.generate_s3_key(author_id, filename, title) if content_type is None: content_type = self._detect_content_type(filename) diff --git a/src/modservice/service/service.py b/src/modservice/service/service.py index e972081..06e9013 100644 --- a/src/modservice/service/service.py +++ b/src/modservice/service/service.py @@ -2,6 +2,7 @@ from modservice.repository.repository import ModRepository from modservice.service.create_mod import create_mod as _create_mod +from modservice.service.get_mods import get_mods as _get_mods from modservice.service.s3_service import S3Service from modservice.service.set_status import set_status as _set_status @@ -12,31 +13,31 @@ def __init__(self, repo: ModRepository, s3_service: S3Service) -> None: self._s3_service = s3_service def create_mod( - self, mod_title: str, author_id: int, description: str + self, title: str, author_id: int, description: str ) -> tuple[int, str, str]: return _create_mod( self._repo, self._s3_service, - mod_title, + title, author_id, description, ) def generate_s3_key( - self, author_id: int, filename: str, mod_title: str | None = None + self, author_id: int, filename: str, title: str | None = None ) -> str: - return self._s3_service.generate_s3_key(author_id, filename, mod_title) + return self._s3_service.generate_s3_key(author_id, filename, title) def generate_upload_url( self, author_id: int, filename: str, - mod_title: str | None = None, + title: str | None = None, expiration: int = 3600, content_type: str | None = None, ) -> tuple[str, str]: return self._s3_service.generate_upload_url( - author_id, filename, mod_title, expiration, content_type + author_id, filename, title, expiration, content_type ) def get_file_info_from_s3_key(self, s3_key: str) -> dict[str, Any]: @@ -70,3 +71,8 @@ def get_mod_download_link( def set_status(self, mod_id: int, status: str) -> bool: return _set_status(self._repo, mod_id, status) + + def get_mods( + self, + ) -> list[dict[str, Any]]: + return _get_mods(self._repo) diff --git a/tests/service/test_s3_service.py b/tests/service/test_s3_service.py index 72a90c7..8eff9a1 100644 --- a/tests/service/test_s3_service.py +++ b/tests/service/test_s3_service.py @@ -32,15 +32,13 @@ def test_generate_s3_key_basic(self, s3_service: S3Service) -> None: assert s3_key.endswith("_test_mod.zip") assert "_" in s3_key # Должен содержать timestamp - def test_generate_s3_key_with_mod_title( - self, s3_service: S3Service - ) -> None: + def test_generate_s3_key_with_title(self, s3_service: S3Service) -> None: """Тест генерации S3 ключа с заголовком мода""" author_id = 456 filename = "awesome_mod.rar" - mod_title = "Awesome Mod v2.0" + title = "Awesome Mod v2.0" - s3_key = s3_service.generate_s3_key(author_id, filename, mod_title) + s3_key = s3_service.generate_s3_key(author_id, filename, title) assert s3_key.startswith(f"{author_id}/") assert s3_key.endswith("_Awesome_Mod_v2.0.rar") @@ -52,9 +50,9 @@ def test_generate_s3_key_special_characters( """Тест очистки специальных символов в S3 ключе""" author_id = 789 filename = "test@mod#.zip" - mod_title = "Test@Mod# v1.0!" + title = "Test@Mod# v1.0!" - s3_key = s3_service.generate_s3_key(author_id, filename, mod_title) + s3_key = s3_service.generate_s3_key(author_id, filename, title) assert s3_key.startswith(f"{author_id}/") assert s3_key.endswith("_TestMod_v1.0.zip") @@ -82,7 +80,7 @@ def test_generate_upload_url( """Тест генерации URL для загрузки с автогенерацией ключа""" author_id = 123 filename = "test_mod.zip" - mod_title = "Test Mod" + title = "Test Mod" expiration = 7200 content_type = "application/zip" @@ -92,7 +90,7 @@ def test_generate_upload_url( ) s3_key, presigned_url = s3_service.generate_upload_url( - author_id, filename, mod_title, expiration, content_type + author_id, filename, title, expiration, content_type ) assert s3_key.startswith(f"{author_id}/") diff --git a/tests/service/test_service.py b/tests/service/test_service.py index d584b34..2d13cb7 100644 --- a/tests/service/test_service.py +++ b/tests/service/test_service.py @@ -28,16 +28,16 @@ def test_generate_s3_key( ) -> None: author_id = 123 filename = "test_mod.zip" - mod_title = "Test Mod" + title = "Test Mod" expected_key = "mods/123/20231201_120000_Test_Mod.zip" mock_s3_service.generate_s3_key.return_value = expected_key - result = mod_service.generate_s3_key(author_id, filename, mod_title) + result = mod_service.generate_s3_key(author_id, filename, title) assert result == expected_key mock_s3_service.generate_s3_key.assert_called_once_with( - author_id, filename, mod_title + author_id, filename, title ) def test_generate_upload_url( @@ -45,7 +45,7 @@ def test_generate_upload_url( ) -> None: author_id = 456 filename = "awesome_mod.rar" - mod_title = "Awesome Mod" + title = "Awesome Mod" expiration = 7200 content_type = "application/x-rar-compressed" @@ -58,14 +58,14 @@ def test_generate_upload_url( ) s3_key, presigned_url = mod_service.generate_upload_url( - author_id, filename, mod_title, expiration, content_type + author_id, filename, title, expiration, content_type ) assert s3_key == expected_s3_key assert presigned_url == expected_url mock_s3_service.generate_upload_url.assert_called_once_with( - author_id, filename, mod_title, expiration, content_type + author_id, filename, title, expiration, content_type ) def test_get_file_info_from_s3_key( @@ -91,7 +91,7 @@ def test_get_file_info_from_s3_key( def test_create_mod_delegates_to_repository( self, mod_service: ModService ) -> None: - mod_title = "Test Mod" + title = "Test Mod" author_id = 123 description = "Test description" @@ -109,6 +109,6 @@ def test_create_mod_delegates_to_repository( mock_create_mod, ) - result = mod_service.create_mod(mod_title, author_id, description) + result = mod_service.create_mod(title, author_id, description) assert result == expected_result