Skip to content
Closed
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
3 changes: 2 additions & 1 deletion src/example/comment/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
CreateCommentUseCase,
DeleteCommentInput,
DeleteCommentUseCase,
GetCommentInput,
GetCommentUseCase,
ListCommentsInput,
ListCommentsUseCase,
Expand Down Expand Up @@ -62,7 +63,7 @@ async def list_comments(note_id: int, request: Request) -> JSONResponse:

@router.get("/{comment_id}")
async def get_comment(note_id: int, comment_id: int) -> JSONResponse:
comment = get_use_case.execute(comment_id)
comment = get_use_case.execute(GetCommentInput(comment_id))
return JSONResponse(_comment_dict(comment))

@router.post("", status_code=201)
Expand Down
11 changes: 8 additions & 3 deletions src/example/comment/use_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,19 @@ def execute(self, input_: ListCommentsInput) -> ListCommentsOutput:
)


@dataclass(frozen=True, slots=True)
class GetCommentInput:
comment_id: int


class GetCommentUseCase:
def __init__(self, repository: CommentRepositoryInterface) -> None:
self._repository = repository

def execute(self, comment_id: int) -> Comment:
comment = self._repository.find_by_id(comment_id)
def execute(self, input_: GetCommentInput) -> Comment:
comment = self._repository.find_by_id(input_.comment_id)
if comment is None:
raise CommentNotFoundException(comment_id)
raise CommentNotFoundException(input_.comment_id)
return comment


Expand Down
9 changes: 6 additions & 3 deletions src/example/mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
CreateCommentUseCase,
DeleteCommentInput,
DeleteCommentUseCase,
GetCommentInput,
GetCommentUseCase,
ListCommentsInput,
ListCommentsUseCase,
Expand All @@ -26,6 +27,7 @@
CreateNoteUseCase,
DeleteNoteInput,
DeleteNoteUseCase,
GetNoteInput,
GetNoteUseCase,
ListNotesInput,
ListNotesUseCase,
Expand All @@ -37,6 +39,7 @@
CreateTagUseCase,
DeleteTagInput,
DeleteTagUseCase,
GetTagInput,
GetTagUseCase,
ListTagsInput,
ListTagsUseCase,
Expand Down Expand Up @@ -80,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]
Expand All @@ -102,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]
Expand All @@ -126,7 +129,7 @@ def list_comments(note_id: int, limit: int = 20, offset: int = 0) -> list[dict]:

@server.tool("Get a single comment by ID.")
def get_comment(comment_id: int) -> dict: # type: ignore[type-arg]
return asdict(comment_get.execute(comment_id))
return asdict(comment_get.execute(GetCommentInput(comment_id=comment_id)))

@server.tool("Create a new comment on a note.")
def create_comment(note_id: int, body: str) -> dict: # type: ignore[type-arg]
Expand Down
3 changes: 2 additions & 1 deletion src/example/note/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
CreateNoteUseCase,
DeleteNoteInput,
DeleteNoteUseCase,
GetNoteInput,
GetNoteUseCase,
ListNotesInput,
ListNotesUseCase,
Expand Down Expand Up @@ -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)
Expand Down
11 changes: 8 additions & 3 deletions src/example/note/use_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down
3 changes: 2 additions & 1 deletion src/example/tag/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
CreateTagUseCase,
DeleteTagInput,
DeleteTagUseCase,
GetTagInput,
GetTagUseCase,
ListTagsInput,
ListTagsUseCase,
Expand Down Expand Up @@ -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)
Expand Down
11 changes: 8 additions & 3 deletions src/example/tag/use_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down
7 changes: 4 additions & 3 deletions tests/example/comment/test_comment_use_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
CreateCommentUseCase,
DeleteCommentInput,
DeleteCommentUseCase,
GetCommentInput,
GetCommentUseCase,
ListCommentsInput,
ListCommentsUseCase,
Expand Down Expand Up @@ -41,14 +42,14 @@ def test_list_filters_by_note_id() -> None:
def test_get_returns_comment() -> None:
repo = _repo()
comment = CreateCommentUseCase(repo).execute(CreateCommentInput(note_id=1, body="body"))
fetched = GetCommentUseCase(repo).execute(comment.id)
fetched = GetCommentUseCase(repo).execute(GetCommentInput(comment_id=comment.id))
assert fetched == comment


def test_get_raises_when_not_found() -> None:
repo = _repo()
with pytest.raises(CommentNotFoundException):
GetCommentUseCase(repo).execute(9999)
GetCommentUseCase(repo).execute(GetCommentInput(comment_id=9999))


def test_update_changes_body() -> None:
Expand All @@ -71,7 +72,7 @@ def test_delete_removes_comment() -> None:
comment = CreateCommentUseCase(repo).execute(CreateCommentInput(note_id=1, body="bye"))
DeleteCommentUseCase(repo).execute(DeleteCommentInput(comment_id=comment.id))
with pytest.raises(CommentNotFoundException):
GetCommentUseCase(repo).execute(comment.id)
GetCommentUseCase(repo).execute(GetCommentInput(comment_id=comment.id))


def test_delete_raises_when_not_found() -> None:
Expand Down
3 changes: 2 additions & 1 deletion tests/example/test_mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
CreateNoteUseCase,
DeleteNoteInput,
DeleteNoteUseCase,
GetNoteInput,
GetNoteUseCase,
ListNotesInput,
ListNotesUseCase,
Expand Down Expand Up @@ -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
Loading