From 644bcbe59cca6c0baa935f3add85bcb09985273d Mon Sep 17 00:00:00 2001 From: hideyukiMORI Date: Wed, 20 May 2026 01:41:14 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20Get=20UseCase=20=E3=81=8C=20typed=20Inpu?= =?UTF-8?q?t=20DTO=20=E3=82=92=E4=BD=BF=E3=81=86=E3=82=88=E3=81=86?= =?UTF-8?q?=E7=B5=B1=E4=B8=80=20(#109)=20+=20TRY300=20=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #118 が merge conflict のため手動適用。PR #136 で GetCommentInput は 適用済みのため Note/Tag の GetNoteInput / GetTagInput のみ追加。 合わせて _BoundQueryExecutor.write() の TRY300 違反(return inside try)を修正。 Co-Authored-By: Claude Sonnet 4.6 --- src/example/mcp.py | 6 ++++-- src/example/note/handler.py | 3 ++- src/example/note/use_case.py | 11 ++++++++--- src/example/tag/handler.py | 3 ++- src/example/tag/use_case.py | 11 ++++++++--- src/nene2/database/sqlalchemy_executor.py | 2 +- tests/example/comment/test_comment_http.py | 4 +--- tests/example/test_mcp.py | 3 ++- tests/nene2/middleware/test_request_id.py | 4 +--- 9 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/example/mcp.py b/src/example/mcp.py index d3b99d8..f0f8bf1 100644 --- a/src/example/mcp.py +++ b/src/example/mcp.py @@ -27,6 +27,7 @@ CreateNoteUseCase, DeleteNoteInput, DeleteNoteUseCase, + GetNoteInput, GetNoteUseCase, ListNotesInput, ListNotesUseCase, @@ -38,6 +39,7 @@ CreateTagUseCase, DeleteTagInput, DeleteTagUseCase, + GetTagInput, GetTagUseCase, ListTagsInput, ListTagsUseCase, @@ -81,7 +83,7 @@ def list_notes(limit: int = 20, offset: int = 0) -> list[dict]: # type: ignore[ @server.tool("Get a single note by ID.") def get_note(note_id: int) -> dict: # type: ignore[type-arg] - return asdict(note_get.execute(note_id)) + return asdict(note_get.execute(GetNoteInput(note_id=note_id))) @server.tool("Create a new note.") def create_note(title: str, body: str) -> dict: # type: ignore[type-arg] @@ -103,7 +105,7 @@ def list_tags(limit: int = 20, offset: int = 0) -> list[dict]: # type: ignore[t @server.tool("Get a single tag by ID.") def get_tag(tag_id: int) -> dict: # type: ignore[type-arg] - return asdict(tag_get.execute(tag_id)) + return asdict(tag_get.execute(GetTagInput(tag_id=tag_id))) @server.tool("Create a new tag.") def create_tag(name: str) -> dict: # type: ignore[type-arg] diff --git a/src/example/note/handler.py b/src/example/note/handler.py index df6c84e..49e1ff4 100644 --- a/src/example/note/handler.py +++ b/src/example/note/handler.py @@ -12,6 +12,7 @@ CreateNoteUseCase, DeleteNoteInput, DeleteNoteUseCase, + GetNoteInput, GetNoteUseCase, ListNotesInput, ListNotesUseCase, @@ -54,7 +55,7 @@ async def list_notes(request: Request) -> JSONResponse: @router.get("/{note_id}") async def get_note(note_id: int) -> JSONResponse: - note = get_use_case.execute(note_id) + note = get_use_case.execute(GetNoteInput(note_id)) return JSONResponse({"id": note.id, "title": note.title, "body": note.body}) @router.post("", status_code=201) diff --git a/src/example/note/use_case.py b/src/example/note/use_case.py index ff1bc4b..9a934b2 100644 --- a/src/example/note/use_case.py +++ b/src/example/note/use_case.py @@ -50,14 +50,19 @@ def execute(self, input_: CreateNoteInput) -> Note: return self._repository.save(input_.title, input_.body) +@dataclass(frozen=True, slots=True) +class GetNoteInput: + note_id: int + + class GetNoteUseCase: def __init__(self, repository: NoteRepositoryInterface) -> None: self._repository = repository - def execute(self, note_id: int) -> Note: - note = self._repository.find_by_id(note_id) + def execute(self, input_: GetNoteInput) -> Note: + note = self._repository.find_by_id(input_.note_id) if note is None: - raise NoteNotFoundException(note_id) + raise NoteNotFoundException(input_.note_id) return note diff --git a/src/example/tag/handler.py b/src/example/tag/handler.py index 45d9939..0f0ba65 100644 --- a/src/example/tag/handler.py +++ b/src/example/tag/handler.py @@ -12,6 +12,7 @@ CreateTagUseCase, DeleteTagInput, DeleteTagUseCase, + GetTagInput, GetTagUseCase, ListTagsInput, ListTagsUseCase, @@ -52,7 +53,7 @@ async def list_tags(request: Request) -> JSONResponse: @router.get("/{tag_id}") async def get_tag(tag_id: int) -> JSONResponse: - tag = get_use_case.execute(tag_id) + tag = get_use_case.execute(GetTagInput(tag_id)) return JSONResponse({"id": tag.id, "name": tag.name}) @router.post("", status_code=201) diff --git a/src/example/tag/use_case.py b/src/example/tag/use_case.py index 1c429c6..6763cee 100644 --- a/src/example/tag/use_case.py +++ b/src/example/tag/use_case.py @@ -36,14 +36,19 @@ def execute(self, input_: ListTagsInput) -> ListTagsOutput: ) +@dataclass(frozen=True, slots=True) +class GetTagInput: + tag_id: int + + class GetTagUseCase: def __init__(self, repository: TagRepositoryInterface) -> None: self._repository = repository - def execute(self, tag_id: int) -> Tag: - tag = self._repository.find_by_id(tag_id) + def execute(self, input_: GetTagInput) -> Tag: + tag = self._repository.find_by_id(input_.tag_id) if tag is None: - raise TagNotFoundException(tag_id) + raise TagNotFoundException(input_.tag_id) return tag diff --git a/src/nene2/database/sqlalchemy_executor.py b/src/nene2/database/sqlalchemy_executor.py index b83d470..fd63f48 100644 --- a/src/nene2/database/sqlalchemy_executor.py +++ b/src/nene2/database/sqlalchemy_executor.py @@ -82,9 +82,9 @@ def fetch_one(self, sql: str, params: dict[str, Any] | None = None) -> dict[str, def write(self, sql: str, params: dict[str, Any] | None = None) -> int: try: result = self._conn.execute(text(sql), params or {}) - return result.lastrowid or result.rowcount except OperationalError as exc: raise DatabaseConnectionException(str(exc)) from exc + return result.lastrowid or result.rowcount class SqlAlchemyTransactionManager(DatabaseTransactionManagerInterface): diff --git a/tests/example/comment/test_comment_http.py b/tests/example/comment/test_comment_http.py index 9616473..1baefcf 100644 --- a/tests/example/comment/test_comment_http.py +++ b/tests/example/comment/test_comment_http.py @@ -56,9 +56,7 @@ def test_get_comment_not_found() -> None: def test_update_comment() -> None: client, note_id = _client_with_note() created = client.post(f"/notes/{note_id}/comments", json={"body": "original"}).json() - response = client.put( - f"/notes/{note_id}/comments/{created['id']}", json={"body": "updated"} - ) + response = client.put(f"/notes/{note_id}/comments/{created['id']}", json={"body": "updated"}) assert response.status_code == 200 assert response.json()["body"] == "updated" diff --git a/tests/example/test_mcp.py b/tests/example/test_mcp.py index c8c26d4..7d99b26 100644 --- a/tests/example/test_mcp.py +++ b/tests/example/test_mcp.py @@ -11,6 +11,7 @@ CreateNoteUseCase, DeleteNoteInput, DeleteNoteUseCase, + GetNoteInput, GetNoteUseCase, ListNotesInput, ListNotesUseCase, @@ -44,7 +45,7 @@ def test_note_lifecycle_via_use_cases() -> None: note = create_uc.execute(CreateNoteInput(title="MCP note", body="content")) assert list_uc.execute(ListNotesInput(limit=10, offset=0)).total == 1 - fetched = get_uc.execute(note.id) + fetched = get_uc.execute(GetNoteInput(note_id=note.id)) assert fetched.title == "MCP note" delete_uc.execute(DeleteNoteInput(note_id=note.id)) assert list_uc.execute(ListNotesInput(limit=10, offset=0)).total == 0 diff --git a/tests/nene2/middleware/test_request_id.py b/tests/nene2/middleware/test_request_id.py index 889392d..c4789e3 100644 --- a/tests/nene2/middleware/test_request_id.py +++ b/tests/nene2/middleware/test_request_id.py @@ -8,9 +8,7 @@ from nene2.middleware import RequestIdMiddleware, request_id_var -_UUID_V4_RE = re.compile( - r"^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" -) +_UUID_V4_RE = re.compile(r"^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$") def _make_app() -> FastAPI: