Skip to content

Commit ea763df

Browse files
authored
Merge pull request #9 from getmarkus/cm-branch-8
refactor: change async methods to synchronous in repositories
2 parents 17c5cf3 + b8706d0 commit ea763df

File tree

13 files changed

+74
-109
lines changed

13 files changed

+74
-109
lines changed

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,13 @@ Temporary Items
192192
.apdisk
193193

194194
### macOS Patch ###
195+
# End of https://www.toptal.com/developers/gitignore/api/macos
195196
# iCloud generated files
196197
*.icloud
197198

198-
# End of https://www.toptal.com/developers/gitignore/api/macos
199+
# SQLite database files
200+
*.db
201+
*.sqlite
202+
*.sqlite3
203+
*.sqlite-journal
204+
*.sqlite3-journal

config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class Settings(BaseSettings):
1717
env_smoke_test: str = ""
1818
project_name: str = ""
1919
database_url: str = "sqlite:///./issues.db"
20+
migrate_database: bool = True
2021
# BACKEND_CORS_ORIGINS is a JSON-formatted list of origins
2122
# e.g: '["http://localhost", "http://localhost:4200", "http://localhost:3000", \
2223
# "http://localhost:8080", "http://local.dockertoolbox.tiangolo.com"]'

main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from src.interface_adapters import api_router
1515
from src.interface_adapters.exceptions import AppException
1616
from src.interface_adapters.middleware.error_handler import app_exception_handler
17+
from src.resource_adapters.persistence.sqlmodel.database import init_db
1718

1819
# https://brandur.org/logfmt
1920
# https://github.com/Delgan/loguru
@@ -36,8 +37,7 @@ async def lifespan(app: FastAPI):
3637
logger.info("Lifespan started")
3738

3839
# Initialize database if using SQLModel
39-
if Settings.get_settings().execution_mode == "sqlmodel":
40-
from src.resource_adapters.persistence.sqlmodel.database import init_db
40+
if Settings.get_settings().execution_mode == "sqlmodel" and not Settings.get_settings().migrate_database:
4141
init_db()
4242

4343
yield

src/app/ports/repositories/issues.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,26 @@
33

44
class IssueRepository(Protocol):
55
# or get
6-
async def get_by_id(self, id: int) -> Issue:
6+
def get_by_id(self, id: int) -> Issue:
77
...
88

99
# or get_all
10-
async def list(self) -> Iterable[Issue]:
10+
def list(self) -> Iterable[Issue]:
1111
# return []
1212
...
1313

14-
async def list_with_predicate(self, predicate: Callable[[Issue], bool]) -> Iterable[Issue]:
14+
def list_with_predicate(self, predicate: Callable[[Issue], bool]) -> Iterable[Issue]:
1515
# for item in self.list():
1616
# if predicate(item):
1717
# yield item
1818
# return filter(predicate, self.list())
1919
...
2020

21-
async def add(self, entity: Issue) -> None:
21+
def add(self, entity: Issue) -> None:
2222
...
2323

24-
async def update(self, entity: Issue) -> None:
24+
def update(self, entity: Issue) -> None:
2525
...
2626

27-
async def remove(self, entity: Issue) -> None:
27+
def remove(self, entity: Issue) -> None:
2828
...

src/app/repository.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ def __enter__(self) -> "UnitOfWork":
1414

1515
def __exit__(
1616
self,
17-
exc_type: type[BaseException],
18-
exc_val: BaseException,
19-
exc_tb: TracebackType,
17+
exc_type: type[BaseException] | None,
18+
exc_val: BaseException | None,
19+
exc_tb: TracebackType | None,
2020
) -> None:
2121
...

src/app/usecases/analyze_issue.py

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,13 @@
1-
from abc import abstractmethod
21
from typing import Protocol
32

43
from loguru import logger
54

65
from src.app.ports.repositories.issues import IssueRepository
7-
from src.app.repository import UnitOfWork
86
from src.domain.issue import Issue
97
from src.interface_adapters.exceptions import NotFoundException
108

119

12-
class AnalyzeIssueProtocol(Protocol):
13-
@abstractmethod
14-
async def analyze(self) -> Issue:
15-
raise NotImplementedError
16-
17-
18-
class AnalyzeIssue(AnalyzeIssueProtocol):
10+
class AnalyzeIssue(Protocol):
1911
# or could be a DTO as a inner class
2012
# or could be a empty/minimal Issue object
2113
issue_number: int = 0
@@ -24,18 +16,16 @@ def __init__(
2416
self,
2517
issue_number: int,
2618
repo: IssueRepository,
27-
unit_of_work: UnitOfWork,
2819
) -> None:
2920
self.issue_number = issue_number
3021
self.repo = repo
31-
self.unit_of_work = unit_of_work
3222

33-
async def analyze(self) -> Issue:
23+
def analyze(self) -> Issue:
3424
logger.info(f"analyzing issue: {self.issue_number}")
35-
issue = await self.repo.get_by_id(self.issue_number)
25+
issue = self.repo.get_by_id(self.issue_number)
3626
if issue.issue_number == 0:
3727
raise NotFoundException(
3828
message="Issue not found",
39-
detail=f"Issue with number {self.issue_number} does not exist"
29+
detail=f"Issue with number {self.issue_number} does not exist",
4030
)
4131
return issue

src/interface_adapters/endpoints/issues.py

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,30 @@
22

33
from fastapi import APIRouter, Depends
44
from loguru import logger
5+
from sqlmodel import Session
56

67
from config import Settings
78
from src.app.ports.repositories.issues import IssueRepository
8-
from src.app.repository import UnitOfWork as BaseUnitOfWork
99
from src.app.usecases.analyze_issue import AnalyzeIssue
1010
from src.domain.issue import Issue
1111
from src.interface_adapters.exceptions import UnsupportedOperationException
1212
from src.resource_adapters.persistence.in_memory.issues import InMemoryIssueRepository
13-
from src.resource_adapters.persistence.in_memory.unit_of_work import (
14-
UnitOfWork as InMemoryUnitOfWork,
15-
)
13+
from src.resource_adapters.persistence.sqlmodel.database import get_db
1614
from src.resource_adapters.persistence.sqlmodel.issues import SQLModelIssueRepository
17-
from src.resource_adapters.persistence.sqlmodel.unit_of_work import SQLModelUnitOfWork
1815

1916
router = APIRouter()
2017

2118

2219
# https://fastapi.tiangolo.com/tutorial/dependencies/
23-
async def configure_unit_of_work() -> BaseUnitOfWork:
24-
execution_mode = Settings.get_settings().execution_mode
25-
if execution_mode == "in-memory":
26-
return InMemoryUnitOfWork()
27-
elif execution_mode == "sqlmodel":
28-
return SQLModelUnitOfWork(database_url=Settings.get_settings().database_url)
29-
else:
30-
raise UnsupportedOperationException(
31-
message="Unsupported unit of work configuration",
32-
detail=f"Execution mode '{execution_mode}' is not supported",
33-
)
34-
35-
36-
async def configure_repository() -> IssueRepository:
20+
def configure_repository(
21+
session: Annotated[Session, Depends(get_db)]
22+
#session: SessionDep,
23+
) -> IssueRepository:
3724
execution_mode = Settings.get_settings().execution_mode
3825
if execution_mode == "in-memory":
3926
return InMemoryIssueRepository()
4027
elif execution_mode == "sqlmodel":
41-
# For SQLModel, the repository is managed by the UnitOfWork
42-
# This is just a placeholder that will be replaced
43-
return SQLModelIssueRepository(None) # type: ignore
28+
return SQLModelIssueRepository(session)
4429
else:
4530
raise UnsupportedOperationException(
4631
message="Unsupported repository configuration",
@@ -49,18 +34,10 @@ async def configure_repository() -> IssueRepository:
4934

5035

5136
@router.post("/issues/{issue_number}/analyze", response_model=Issue)
52-
async def analyze_issue(
37+
def analyze_issue(
5338
issue_number: int,
54-
unit_of_work: Annotated[BaseUnitOfWork, Depends(configure_unit_of_work)],
55-
repo: Annotated[IssueRepository, Depends(configure_repository)],
39+
repo: Annotated[IssueRepository, Depends(configure_repository)]
5640
) -> Issue:
5741
logger.info(f"analyzing issue: {issue_number}")
58-
59-
# For SQLModel, we need to use the repository from the unit of work
60-
if isinstance(unit_of_work, SQLModelUnitOfWork):
61-
repo = unit_of_work.issues
62-
63-
use_case = AnalyzeIssue(
64-
issue_number=issue_number, repo=repo, unit_of_work=unit_of_work
65-
)
66-
return await use_case.analyze()
42+
use_case = AnalyzeIssue(issue_number=issue_number, repo=repo)
43+
return use_case.analyze()

src/interface_adapters/middleware/error_handler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from src.interface_adapters.exceptions import AppException
66

77

8-
async def app_exception_handler(request: Request, exc: Exception) -> JSONResponse:
8+
def app_exception_handler(request: Request, exc: Exception) -> JSONResponse:
99
if isinstance(exc, AppException):
1010
logger.error(f"Request to {request.url} failed: {exc.message}")
1111
if exc.detail:

src/resource_adapters/persistence/in_memory/issues.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@
44

55
from src.app.ports.repositories.issues import IssueRepository
66
from src.domain.issue import Issue
7+
from src.resource_adapters.persistence.in_memory.unit_of_work import InMemoryUnitOfWork
78

89

9-
class InMemoryIssueRepository(IssueRepository):
10+
class InMemoryIssueRepository(InMemoryUnitOfWork, IssueRepository):
1011
def __init__(self) -> None:
1112
self.issues: List[Issue] = []
1213
# self.issues: dict[int, Issue] = {}
1314
# self.data = {}
1415

15-
async def get_by_id(self, id: int) -> Issue:
16+
def get_by_id(self, id: int) -> Issue:
1617
logger.info(f"getting issue by id: {id}")
1718
for issue in self.issues:
1819
if issue.issue_number == id:
@@ -22,7 +23,7 @@ async def get_by_id(self, id: int) -> Issue:
2223
# copy.deepcopy(self.issues[id])
2324
# return self.data.get(key)
2425

25-
async def list(self) -> List[Issue]:
26+
def list(self) -> List[Issue]:
2627
return self.issues
2728
# return self.issues.values()
2829

@@ -33,13 +34,11 @@ def name_starts_with_a(user: User) -> bool:
3334
users_with_a = user_repo.list_with_predicate(name_starts_with_a)
3435
"""
3536

36-
async def list_with_predicate(
37-
self, predicate: Callable[[Issue], bool]
38-
) -> List[Issue]:
37+
def list_with_predicate(self, predicate: Callable[[Issue], bool]) -> List[Issue]:
3938
return [issue for issue in self.issues if predicate(issue)]
4039
# return filter(predicate, self.issues.values())
4140

42-
async def add(self, entity: Issue) -> None:
41+
def add(self, entity: Issue) -> None:
4342
logger.info(f"adding issue: {entity.issue_number}")
4443
self.issues.append(entity)
4544
# self.issues[entity.number] = entity
@@ -48,15 +47,15 @@ async def add(self, entity: Issue) -> None:
4847
# self.data[key] = value
4948
# return True
5049

51-
async def update(self, entity: Issue) -> None:
50+
def update(self, entity: Issue) -> None:
5251
logger.info(f"updating issue: {entity}")
5352
for i, issue in enumerate(self.issues):
5453
if issue.issue_number == entity.issue_number:
5554
self.issues[i] = entity
5655
break
5756
# self.issues[entity.issue_number] = entity
5857

59-
async def remove(self, entity: Issue) -> None:
58+
def remove(self, entity: Issue) -> None:
6059
logger.info(f"removing issue: {entity}")
6160
self.issues = [i for i in self.issues if i.issue_number != entity.issue_number]
6261
# if entity in self.issues:

src/resource_adapters/persistence/in_memory/unit_of_work.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from src.app.repository import UnitOfWork
66

77

8-
class UnitOfWork(UnitOfWork):
8+
class InMemoryUnitOfWork(UnitOfWork):
99
def __init__(self) -> None:
1010
self.committed = False
1111

@@ -15,15 +15,15 @@ def commit(self) -> None:
1515
def rollback(self) -> None:
1616
self.committed = False
1717

18-
def __enter__(self) -> "UnitOfWork":
18+
def __enter__(self) -> "InMemoryUnitOfWork":
1919
logger.info("enter uow")
2020
return self
2121

2222
def __exit__(
2323
self,
24-
exc_type: type[BaseException],
25-
exc_val: BaseException,
26-
exc_tb: TracebackType,
24+
exc_type: type[BaseException] | None,
25+
exc_val: BaseException | None,
26+
exc_tb: TracebackType | None,
2727
) -> None:
2828
if exc_type:
2929
logger.info("exit rollback uow")

0 commit comments

Comments
 (0)