Skip to content

Commit 5006d91

Browse files
committed
feat(user): concrete implementation of UserRepository using Postgres
1 parent 573cd35 commit 5006d91

File tree

7 files changed

+91
-2
lines changed

7 files changed

+91
-2
lines changed

src/shared/infra/persistence/sqlalchemy/session_maker.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
Base,
66
)
77

8+
from src.shared.infra.settings import Settings
9+
810

911
class SessionMaker:
1012
_session_maker: sessionmaker[Session]
1113
_engine: Engine
1214

13-
def __init__(self, url: str) -> None:
14-
self._engine = create_engine(url)
15+
def __init__(self, settings: Settings) -> None:
16+
self._engine = create_engine(settings.postgres_url)
1517
self._session_maker = sessionmaker(bind=self._engine)
1618

1719
def get_session(self) -> Session:

src/shared/infra/settings.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from pydantic import Field
2+
from pydantic_settings import BaseSettings, SettingsConfigDict
3+
4+
5+
class Settings(BaseSettings):
6+
model_config = SettingsConfigDict(env_file=(".env", ".env.prod"), extra="ignore")
7+
postgres_user: str = Field(alias="POSTGRES_USER")
8+
postgres_password: str = Field(alias="POSTGRES_PASSWORD")
9+
postgres_db: str = Field(alias="POSTGRES_DB")
10+
postgres_host: str = Field(alias="POSTGRES_HOST")
11+
postgres_port: str = Field(alias="POSTGRES_PORT")
12+
13+
@property
14+
def postgres_url(self) -> str:
15+
return f"postgresql://{self.postgres_user}:{self.postgres_password}@{self.postgres_host}:{self.postgres_port}/{self.postgres_db}"

src/social/user/domain/user.py

+8
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,11 @@ def signup(cls, id_: str, name: str, username: str, email: str) -> "User":
2929
username=UserUsername(username),
3030
email=UserEmail(email),
3131
)
32+
33+
def to_dict(self) -> dict:
34+
return {
35+
"id": self.id_.value,
36+
"name": self.name.value,
37+
"username": self.username.value,
38+
"email": self.email.value,
39+
}

src/social/user/infra/persistence/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from typing import override
2+
3+
from src.shared.infra.persistence.sqlalchemy.session_maker import SessionMaker
4+
from src.social.user.domain.user import User
5+
from src.social.user.domain.user_id import UserId
6+
from src.social.user.domain.user_repository import UserRepository
7+
from src.social.user.infra.persistence.user_model import UserModel
8+
9+
10+
class PostgresUserRepository(UserRepository):
11+
_session_maker: SessionMaker
12+
13+
def __init__(self, session_maker: SessionMaker) -> None:
14+
self._session_maker = session_maker
15+
16+
@override
17+
async def save(self, user: User) -> None:
18+
async with self._session_maker.get_session() as session:
19+
user_to_save = UserModel(**user.to_dict())
20+
session.add(user_to_save)
21+
await session.commit()
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from src.shared.infra.persistence.sqlalchemy.base import Base
2+
from sqlalchemy import UUID, String
3+
from sqlalchemy.orm import mapped_column, Mapped
4+
5+
6+
class UserModel(Base):
7+
__tablename__ = "users"
8+
9+
id: Mapped[UUID] = mapped_column(UUID(as_uuid=True), primary_key=True)
10+
name: Mapped[str] = mapped_column(String, nullable=False)
11+
username: Mapped[str] = mapped_column(String, nullable=False, unique=True)
12+
email: Mapped[str] = mapped_column(String, nullable=False, unique=True)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import pytest
2+
3+
from src.shared.infra.persistence.sqlalchemy.session_maker import SessionMaker
4+
from src.shared.infra.settings import Settings
5+
from src.social.user.infra.persistence.postgres_user_repository import (
6+
PostgresUserRepository,
7+
)
8+
from src.social.user.infra.persistence.user_model import UserModel
9+
from tests.social.user.domain.user_mother import UserMother
10+
11+
12+
@pytest.mark.integration
13+
class TestPostgresUserRepository:
14+
@classmethod
15+
def setup_class(cls) -> None:
16+
cls.session_maker = SessionMaker(settings=Settings())
17+
cls.session_maker.create_tables()
18+
19+
@classmethod
20+
def teardown_class(cls) -> None:
21+
with cls.session_maker.get_session() as session:
22+
session.query(UserModel).delete()
23+
session.commit()
24+
25+
@pytest.mark.asyncio
26+
async def test_should_save_user(self) -> None:
27+
repository = PostgresUserRepository(session_maker=self.session_maker)
28+
user = UserMother.any()
29+
30+
await repository.save(user)

0 commit comments

Comments
 (0)