feat: add list_bounties and list_all_bounties methods to storage adapter
Add filtering methods to JsonFileRoomStorage for Phase 2 soft delete support: - list_bounties(room_id): returns only non-deleted bounties for normal queries - list_all_bounties(room_id, include_deleted=True): returns all bounties for /recover Update RoomStorage protocol to include the new methods. Update mock classes in tests to pass isinstance checks. Fixes #42
This commit is contained in:
@@ -140,6 +140,35 @@ class JsonFileRoomStorage:
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def list_bounties(self, room_id: int) -> list[Bounty]:
|
||||||
|
"""List all non-deleted bounties in a room.
|
||||||
|
|
||||||
|
This is the default method for normal queries - soft-deleted bounties
|
||||||
|
are excluded from results.
|
||||||
|
"""
|
||||||
|
room_data = self.load(room_id)
|
||||||
|
if room_data is None:
|
||||||
|
return []
|
||||||
|
return [b for b in room_data.bounties if b.deleted_at is None]
|
||||||
|
|
||||||
|
def list_all_bounties(
|
||||||
|
self, room_id: int, include_deleted: bool = True
|
||||||
|
) -> list[Bounty]:
|
||||||
|
"""List all bounties including or excluding soft-deleted.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
room_id: The room ID
|
||||||
|
include_deleted: If True, return all bounties including soft-deleted.
|
||||||
|
If False, return only non-deleted bounties.
|
||||||
|
Defaults to True for /recover functionality.
|
||||||
|
"""
|
||||||
|
room_data = self.load(room_id)
|
||||||
|
if room_data is None:
|
||||||
|
return []
|
||||||
|
if include_deleted:
|
||||||
|
return room_data.bounties
|
||||||
|
return [b for b in room_data.bounties if b.deleted_at is None]
|
||||||
|
|
||||||
|
|
||||||
class JsonFileTrackingStorage:
|
class JsonFileTrackingStorage:
|
||||||
"""TrackingStorage implementation using JSON files.
|
"""TrackingStorage implementation using JSON files.
|
||||||
|
|||||||
@@ -40,6 +40,25 @@ class RoomStorage(Protocol):
|
|||||||
"""Get a specific bounty from a room by ID."""
|
"""Get a specific bounty from a room by ID."""
|
||||||
...
|
...
|
||||||
|
|
||||||
|
def list_bounties(self, room_id: int) -> list[Bounty]:
|
||||||
|
"""List all non-deleted bounties in a room.
|
||||||
|
|
||||||
|
Soft-deleted bounties (where deleted_at is not None) are excluded.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def list_all_bounties(
|
||||||
|
self, room_id: int, include_deleted: bool = True
|
||||||
|
) -> list[Bounty]:
|
||||||
|
"""List all bounties including or excluding soft-deleted.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
room_id: The room ID
|
||||||
|
include_deleted: If True, return all bounties including soft-deleted.
|
||||||
|
If False, return only non-deleted bounties.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
@runtime_checkable
|
@runtime_checkable
|
||||||
class TrackingStorage(Protocol):
|
class TrackingStorage(Protocol):
|
||||||
|
|||||||
@@ -48,6 +48,20 @@ class SimpleRoomStorage:
|
|||||||
return b
|
return b
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def list_bounties(self, room_id: int) -> list[Bounty]:
|
||||||
|
if room_id not in self._rooms:
|
||||||
|
return []
|
||||||
|
return [b for b in self._rooms[room_id].bounties if b.deleted_at is None]
|
||||||
|
|
||||||
|
def list_all_bounties(
|
||||||
|
self, room_id: int, include_deleted: bool = True
|
||||||
|
) -> list[Bounty]:
|
||||||
|
if room_id not in self._rooms:
|
||||||
|
return []
|
||||||
|
if include_deleted:
|
||||||
|
return self._rooms[room_id].bounties
|
||||||
|
return [b for b in self._rooms[room_id].bounties if b.deleted_at is None]
|
||||||
|
|
||||||
|
|
||||||
class SimpleTrackingStorage:
|
class SimpleTrackingStorage:
|
||||||
"""Minimal mock without ensure_tracking - tests if track_bounty works without it.
|
"""Minimal mock without ensure_tracking - tests if track_bounty works without it.
|
||||||
@@ -119,6 +133,20 @@ class MockRoomStorage:
|
|||||||
return b
|
return b
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def list_bounties(self, room_id: int) -> list[Bounty]:
|
||||||
|
if room_id not in self._rooms:
|
||||||
|
return []
|
||||||
|
return [b for b in self._rooms[room_id].bounties if b.deleted_at is None]
|
||||||
|
|
||||||
|
def list_all_bounties(
|
||||||
|
self, room_id: int, include_deleted: bool = True
|
||||||
|
) -> list[Bounty]:
|
||||||
|
if room_id not in self._rooms:
|
||||||
|
return []
|
||||||
|
if include_deleted:
|
||||||
|
return self._rooms[room_id].bounties
|
||||||
|
return [b for b in self._rooms[room_id].bounties if b.deleted_at is None]
|
||||||
|
|
||||||
|
|
||||||
class MockTrackingStorage:
|
class MockTrackingStorage:
|
||||||
"""Mock implementation of TrackingStorage for testing."""
|
"""Mock implementation of TrackingStorage for testing."""
|
||||||
|
|||||||
Reference in New Issue
Block a user