aiteamutils 0.2.72__tar.gz → 0.2.75__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {aiteamutils-0.2.72 → aiteamutils-0.2.75}/PKG-INFO +1 -1
- {aiteamutils-0.2.72 → aiteamutils-0.2.75}/aiteamutils/base_model.py +9 -2
- aiteamutils-0.2.75/aiteamutils/base_repository.py +141 -0
- aiteamutils-0.2.75/aiteamutils/base_service.py +258 -0
- aiteamutils-0.2.75/aiteamutils/database.py +583 -0
- {aiteamutils-0.2.72 → aiteamutils-0.2.75}/aiteamutils/exceptions.py +6 -2
- {aiteamutils-0.2.72 → aiteamutils-0.2.75}/aiteamutils/security.py +28 -23
- aiteamutils-0.2.75/aiteamutils/version.py +2 -0
- aiteamutils-0.2.72/aiteamutils/base_repository.py +0 -63
- aiteamutils-0.2.72/aiteamutils/base_service.py +0 -78
- aiteamutils-0.2.72/aiteamutils/database.py +0 -244
- aiteamutils-0.2.72/aiteamutils/version.py +0 -2
- {aiteamutils-0.2.72 → aiteamutils-0.2.75}/.cursorrules +0 -0
- {aiteamutils-0.2.72 → aiteamutils-0.2.75}/.gitignore +0 -0
- {aiteamutils-0.2.72 → aiteamutils-0.2.75}/README.md +0 -0
- {aiteamutils-0.2.72 → aiteamutils-0.2.75}/aiteamutils/__init__.py +0 -0
- {aiteamutils-0.2.72 → aiteamutils-0.2.75}/aiteamutils/cache.py +0 -0
- {aiteamutils-0.2.72 → aiteamutils-0.2.75}/aiteamutils/config.py +0 -0
- {aiteamutils-0.2.72 → aiteamutils-0.2.75}/aiteamutils/enums.py +0 -0
- {aiteamutils-0.2.72 → aiteamutils-0.2.75}/aiteamutils/validators.py +0 -0
- {aiteamutils-0.2.72 → aiteamutils-0.2.75}/pyproject.toml +0 -0
- {aiteamutils-0.2.72 → aiteamutils-0.2.75}/setup.py +0 -0
| @@ -23,10 +23,17 @@ class BaseColumn(Base): | |
| 23 23 | 
             
                    doc="ULID",
         | 
| 24 24 | 
             
                    nullable=False
         | 
| 25 25 | 
             
                )
         | 
| 26 | 
            -
                created_at: Mapped[datetime] = mapped_column( | 
| 26 | 
            +
                created_at: Mapped[datetime] = mapped_column(
         | 
| 27 | 
            +
                    default=datetime.utcnow,
         | 
| 28 | 
            +
                    index=True
         | 
| 29 | 
            +
                )
         | 
| 27 30 | 
             
                updated_at: Mapped[datetime] = mapped_column(
         | 
| 28 31 | 
             
                    default=datetime.utcnow,
         | 
| 29 | 
            -
                    onupdate=datetime.utcnow
         | 
| 32 | 
            +
                    onupdate=datetime.utcnow,
         | 
| 33 | 
            +
                    index=True
         | 
| 34 | 
            +
                )
         | 
| 35 | 
            +
                deleted_at: Mapped[datetime] = mapped_column(
         | 
| 36 | 
            +
                    default=None,
         | 
| 30 37 | 
             
                )
         | 
| 31 38 | 
             
                is_deleted: Mapped[bool] = mapped_column(
         | 
| 32 39 | 
             
                    default=False,
         | 
| @@ -0,0 +1,141 @@ | |
| 1 | 
            +
            #기본 라이브러리
         | 
| 2 | 
            +
            from typing import TypeVar, Generic, Type, Any, Dict, List, Optional
         | 
| 3 | 
            +
            from sqlalchemy.orm import DeclarativeBase
         | 
| 4 | 
            +
            from sqlalchemy.ext.asyncio import AsyncSession
         | 
| 5 | 
            +
            from sqlalchemy.exc import SQLAlchemyError
         | 
| 6 | 
            +
            from sqlalchemy import select
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            #패키지 라이브러리
         | 
| 9 | 
            +
            from .exceptions import ErrorCode, CustomException
         | 
| 10 | 
            +
            from .database import (
         | 
| 11 | 
            +
                list_entities,
         | 
| 12 | 
            +
                get_entity,
         | 
| 13 | 
            +
                create_entity,
         | 
| 14 | 
            +
                update_entity,
         | 
| 15 | 
            +
                delete_entity
         | 
| 16 | 
            +
            )
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            ModelType = TypeVar("ModelType", bound=DeclarativeBase)
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            class BaseRepository(Generic[ModelType]):
         | 
| 21 | 
            +
                def __init__(self, session: AsyncSession, model: Type[ModelType]):
         | 
| 22 | 
            +
                    self._session = session
         | 
| 23 | 
            +
                    self.model = model
         | 
| 24 | 
            +
                
         | 
| 25 | 
            +
                @property
         | 
| 26 | 
            +
                def session(self) -> AsyncSession:
         | 
| 27 | 
            +
                    return self._session
         | 
| 28 | 
            +
                
         | 
| 29 | 
            +
                @session.setter
         | 
| 30 | 
            +
                def session(self, value: AsyncSession):
         | 
| 31 | 
            +
                    if value is None:
         | 
| 32 | 
            +
                        raise CustomException(
         | 
| 33 | 
            +
                            ErrorCode.DB_CONNECTION_ERROR,
         | 
| 34 | 
            +
                            detail="Session cannot be None",
         | 
| 35 | 
            +
                            source_function=f"{self.__class__.__name__}.session"
         | 
| 36 | 
            +
                        )
         | 
| 37 | 
            +
                    self._session = value
         | 
| 38 | 
            +
               
         | 
| 39 | 
            +
                #######################
         | 
| 40 | 
            +
                # 입력 및 수정, 삭제 #
         | 
| 41 | 
            +
                ####################### 
         | 
| 42 | 
            +
                async def create(self, entity_data: Dict[str, Any]) -> ModelType:
         | 
| 43 | 
            +
                    try:
         | 
| 44 | 
            +
                        return await create_entity(
         | 
| 45 | 
            +
                            session=self.session,
         | 
| 46 | 
            +
                            model=self.model,
         | 
| 47 | 
            +
                            entity_data=entity_data
         | 
| 48 | 
            +
                        )
         | 
| 49 | 
            +
                    except CustomException as e:
         | 
| 50 | 
            +
                        raise e
         | 
| 51 | 
            +
                    except Exception as e:
         | 
| 52 | 
            +
                        raise CustomException(
         | 
| 53 | 
            +
                            ErrorCode.INTERNAL_ERROR,
         | 
| 54 | 
            +
                            detail=str(e),
         | 
| 55 | 
            +
                            source_function=f"{self.__class__.__name__}.create",
         | 
| 56 | 
            +
                            original_error=e
         | 
| 57 | 
            +
                        )
         | 
| 58 | 
            +
                    
         | 
| 59 | 
            +
                async def update(
         | 
| 60 | 
            +
                    self,
         | 
| 61 | 
            +
                    update_data: Dict[str, Any],
         | 
| 62 | 
            +
                    conditions: Dict[str, Any]
         | 
| 63 | 
            +
                ) -> ModelType:
         | 
| 64 | 
            +
                    try:
         | 
| 65 | 
            +
                        return await update_entity(
         | 
| 66 | 
            +
                            session=self.session,
         | 
| 67 | 
            +
                            model=self.model,
         | 
| 68 | 
            +
                            update_data=update_data,
         | 
| 69 | 
            +
                            conditions=conditions
         | 
| 70 | 
            +
                        )
         | 
| 71 | 
            +
                    except CustomException as e:
         | 
| 72 | 
            +
                        raise e
         | 
| 73 | 
            +
                    
         | 
| 74 | 
            +
                async def delete(
         | 
| 75 | 
            +
                    self,
         | 
| 76 | 
            +
                    conditions: Dict[str, Any]
         | 
| 77 | 
            +
                ) -> None:
         | 
| 78 | 
            +
                    await delete_entity(
         | 
| 79 | 
            +
                        session=self.session,
         | 
| 80 | 
            +
                        model=self.model,
         | 
| 81 | 
            +
                        conditions=conditions
         | 
| 82 | 
            +
                    )
         | 
| 83 | 
            +
                
         | 
| 84 | 
            +
                #########################
         | 
| 85 | 
            +
                # 조회 및 검색 메서드 #
         | 
| 86 | 
            +
                #########################
         | 
| 87 | 
            +
                async def list(
         | 
| 88 | 
            +
                    self,
         | 
| 89 | 
            +
                    skip: int = 0,
         | 
| 90 | 
            +
                    limit: int = 100,
         | 
| 91 | 
            +
                    filters: Optional[Dict[str, Any]] = None,
         | 
| 92 | 
            +
                    explicit_joins: Optional[List[Any]] = None,
         | 
| 93 | 
            +
                    loading_joins: Optional[List[Any]] = None
         | 
| 94 | 
            +
                ) -> List[ModelType]:
         | 
| 95 | 
            +
                    """
         | 
| 96 | 
            +
                    엔티티 목록 조회.
         | 
| 97 | 
            +
                    """
         | 
| 98 | 
            +
                    try:
         | 
| 99 | 
            +
                        # 기본 CRUD 작업 호출
         | 
| 100 | 
            +
                        return await list_entities(
         | 
| 101 | 
            +
                            session=self.session,
         | 
| 102 | 
            +
                            model=self.model,
         | 
| 103 | 
            +
                            skip=skip,
         | 
| 104 | 
            +
                            limit=limit,
         | 
| 105 | 
            +
                            filters=filters,
         | 
| 106 | 
            +
                            explicit_joins=explicit_joins,
         | 
| 107 | 
            +
                            loading_joins=loading_joins
         | 
| 108 | 
            +
                        )
         | 
| 109 | 
            +
                    except CustomException as e:
         | 
| 110 | 
            +
                        raise e
         | 
| 111 | 
            +
                    except Exception as e:
         | 
| 112 | 
            +
                        raise CustomException(
         | 
| 113 | 
            +
                            ErrorCode.INTERNAL_ERROR,
         | 
| 114 | 
            +
                            detail=str(e),
         | 
| 115 | 
            +
                            source_function=f"{self.__class__.__name__}.list",
         | 
| 116 | 
            +
                            original_error=e
         | 
| 117 | 
            +
                        )
         | 
| 118 | 
            +
                    
         | 
| 119 | 
            +
                async def get(
         | 
| 120 | 
            +
                    self,
         | 
| 121 | 
            +
                    conditions: Dict[str, Any] | None = None,
         | 
| 122 | 
            +
                    explicit_joins: Optional[List[Any]] = None,
         | 
| 123 | 
            +
                    loading_joins: Optional[List[Any]] = None
         | 
| 124 | 
            +
                ) -> ModelType:
         | 
| 125 | 
            +
                    try:
         | 
| 126 | 
            +
                        return await get_entity(
         | 
| 127 | 
            +
                            session=self.session,
         | 
| 128 | 
            +
                            model=self.model,
         | 
| 129 | 
            +
                            conditions=conditions,
         | 
| 130 | 
            +
                            explicit_joins=explicit_joins,
         | 
| 131 | 
            +
                            loading_joins=loading_joins
         | 
| 132 | 
            +
                        )
         | 
| 133 | 
            +
                    except CustomException as e:
         | 
| 134 | 
            +
                        raise e
         | 
| 135 | 
            +
                    except Exception as e:
         | 
| 136 | 
            +
                        raise CustomException(
         | 
| 137 | 
            +
                            ErrorCode.INTERNAL_ERROR,
         | 
| 138 | 
            +
                            detail=str(e),
         | 
| 139 | 
            +
                            source_function=f"{self.__class__.__name__}.get",
         | 
| 140 | 
            +
                            original_error=e
         | 
| 141 | 
            +
                        )
         | 
| @@ -0,0 +1,258 @@ | |
| 1 | 
            +
            #기본 라이브러리
         | 
| 2 | 
            +
            from fastapi import Request
         | 
| 3 | 
            +
            from typing import TypeVar, Generic, Type, Dict, Any, Union, List, Optional
         | 
| 4 | 
            +
            from sqlalchemy.orm import DeclarativeBase
         | 
| 5 | 
            +
            from sqlalchemy.ext.asyncio import AsyncSession
         | 
| 6 | 
            +
            from datetime import datetime
         | 
| 7 | 
            +
            from ulid import ULID
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            #패키지 라이브러리
         | 
| 10 | 
            +
            from .exceptions import ErrorCode, CustomException
         | 
| 11 | 
            +
            from .base_repository import BaseRepository
         | 
| 12 | 
            +
            from .database import (
         | 
| 13 | 
            +
                process_response,
         | 
| 14 | 
            +
                validate_unique_fields
         | 
| 15 | 
            +
            )
         | 
| 16 | 
            +
            from .security import hash_password
         | 
| 17 | 
            +
            ModelType = TypeVar("ModelType", bound=DeclarativeBase)
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            class BaseService(Generic[ModelType]):
         | 
| 20 | 
            +
                ##################
         | 
| 21 | 
            +
                # 1. 초기화 영역 #
         | 
| 22 | 
            +
                ##################
         | 
| 23 | 
            +
                def __init__(
         | 
| 24 | 
            +
                        self,
         | 
| 25 | 
            +
                        model: Type[ModelType],
         | 
| 26 | 
            +
                        repository: BaseRepository[ModelType],
         | 
| 27 | 
            +
                        db_session: AsyncSession,
         | 
| 28 | 
            +
                        additional_models: Dict[str, Type[DeclarativeBase]] = None,
         | 
| 29 | 
            +
                ):
         | 
| 30 | 
            +
                    self.model = model
         | 
| 31 | 
            +
                    self.repository = repository
         | 
| 32 | 
            +
                    self.db_session = db_session
         | 
| 33 | 
            +
                    self.additional_models = additional_models or {},
         | 
| 34 | 
            +
                
         | 
| 35 | 
            +
                #######################
         | 
| 36 | 
            +
                # 입력 및 수정, 삭제 #
         | 
| 37 | 
            +
                #######################
         | 
| 38 | 
            +
                async def create(
         | 
| 39 | 
            +
                    self,
         | 
| 40 | 
            +
                    entity_data: Dict[str, Any],
         | 
| 41 | 
            +
                    model_name: str | None = None,
         | 
| 42 | 
            +
                    response_model: Any = None,
         | 
| 43 | 
            +
                    exclude_entities: List[str] | None = None,
         | 
| 44 | 
            +
                    unique_check: List[Dict[str, Any]] | None = None,
         | 
| 45 | 
            +
                    fk_check: List[Dict[str, Any]] | None = None,
         | 
| 46 | 
            +
                ) -> ModelType:
         | 
| 47 | 
            +
                    
         | 
| 48 | 
            +
                    try:
         | 
| 49 | 
            +
                        # 고유 검사 수행
         | 
| 50 | 
            +
                        if unique_check:
         | 
| 51 | 
            +
                            await validate_unique_fields(self.db_session, unique_check, find_value=True)
         | 
| 52 | 
            +
                        # 외래 키 검사 수행
         | 
| 53 | 
            +
                        if fk_check:
         | 
| 54 | 
            +
                            await validate_unique_fields(self.db_session, fk_check, find_value=False)
         | 
| 55 | 
            +
                        # 비밀번호가 있으면 해시 처리
         | 
| 56 | 
            +
                        if "password" in entity_data:
         | 
| 57 | 
            +
                            entity_data["password"] = hash_password(entity_data["password"])
         | 
| 58 | 
            +
                        # 제외할 엔티티가 있으면 제외
         | 
| 59 | 
            +
                        if exclude_entities:
         | 
| 60 | 
            +
                            entity_data = {k: v for k, v in entity_data.items() if k not in exclude_entities}
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                        # repository의 create 메서드를 트랜잭션 내에서 실행
         | 
| 63 | 
            +
                        return await self.repository.create(entity_data)
         | 
| 64 | 
            +
                    except CustomException as e:
         | 
| 65 | 
            +
                        raise e
         | 
| 66 | 
            +
                    except Exception as e:
         | 
| 67 | 
            +
                        # 다른 예외 처리
         | 
| 68 | 
            +
                        raise CustomException(
         | 
| 69 | 
            +
                            ErrorCode.INTERNAL_ERROR,
         | 
| 70 | 
            +
                            detail=str(e),
         | 
| 71 | 
            +
                            source_function=f"{self.__class__.__name__}.create",
         | 
| 72 | 
            +
                            original_error=e
         | 
| 73 | 
            +
                        )
         | 
| 74 | 
            +
                    
         | 
| 75 | 
            +
                async def update(
         | 
| 76 | 
            +
                    self,
         | 
| 77 | 
            +
                    ulid: str | None = None,
         | 
| 78 | 
            +
                    update_data: Dict[str, Any] | None = None,
         | 
| 79 | 
            +
                    conditions: Dict[str, Any] | None = None,
         | 
| 80 | 
            +
                    unique_check: List[Dict[str, Any]] | None = None,
         | 
| 81 | 
            +
                    exclude_entities: List[str] | None = None
         | 
| 82 | 
            +
                ) -> ModelType:
         | 
| 83 | 
            +
                    try:
         | 
| 84 | 
            +
                        # 고유 검사 수행
         | 
| 85 | 
            +
                        if unique_check:
         | 
| 86 | 
            +
                            await validate_unique_fields(self.db_session, unique_check, find_value=True)
         | 
| 87 | 
            +
                        # 비밀번호가 있으면 해시 처리
         | 
| 88 | 
            +
                        if "password" in update_data:
         | 
| 89 | 
            +
                            update_data["password"] = hash_password(update_data["password"])
         | 
| 90 | 
            +
                        # 제외할 엔티티가 있으면 제외
         | 
| 91 | 
            +
                        if exclude_entities:
         | 
| 92 | 
            +
                            update_data = {k: v for k, v in update_data.items() if k not in exclude_entities}
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                        if not ulid and not conditions:
         | 
| 95 | 
            +
                            raise CustomException(
         | 
| 96 | 
            +
                                ErrorCode.INVALID_INPUT,
         | 
| 97 | 
            +
                                detail="Either 'ulid' or 'conditions' must be provided.",
         | 
| 98 | 
            +
                                source_function="database.update_entity"
         | 
| 99 | 
            +
                            )
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                        # ulid로 조건 생성
         | 
| 102 | 
            +
                        if ulid:
         | 
| 103 | 
            +
                            if not ULID.from_str(ulid):
         | 
| 104 | 
            +
                                raise CustomException(
         | 
| 105 | 
            +
                                    ErrorCode.VALIDATION_ERROR,
         | 
| 106 | 
            +
                                    detail=ulid,
         | 
| 107 | 
            +
                                    source_function=f"{self.__class__.__name__}.update"
         | 
| 108 | 
            +
                                )
         | 
| 109 | 
            +
                            
         | 
| 110 | 
            +
                            conditions = {"ulid": ulid}
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                        return await self.repository.update(
         | 
| 113 | 
            +
                            update_data=update_data,
         | 
| 114 | 
            +
                            conditions=conditions
         | 
| 115 | 
            +
                        )
         | 
| 116 | 
            +
                    except CustomException as e:
         | 
| 117 | 
            +
                        raise e
         | 
| 118 | 
            +
                    except Exception as e:
         | 
| 119 | 
            +
                        raise CustomException(
         | 
| 120 | 
            +
                            ErrorCode.INTERNAL_ERROR,
         | 
| 121 | 
            +
                            detail=str(e),
         | 
| 122 | 
            +
                            source_function=f"{self.__class__.__name__}.update",
         | 
| 123 | 
            +
                            original_error=e
         | 
| 124 | 
            +
                        )
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                async def delete(
         | 
| 127 | 
            +
                    self,
         | 
| 128 | 
            +
                    ulid: str | None = None,
         | 
| 129 | 
            +
                    conditions: Dict[str, Any] | None = None
         | 
| 130 | 
            +
                ) -> None:
         | 
| 131 | 
            +
                    try:
         | 
| 132 | 
            +
                        if not ULID.from_str(ulid):
         | 
| 133 | 
            +
                            raise CustomException(
         | 
| 134 | 
            +
                                ErrorCode.VALIDATION_ERROR,
         | 
| 135 | 
            +
                                detail=ulid,
         | 
| 136 | 
            +
                                source_function=f"{self.__class__.__name__}.delete"
         | 
| 137 | 
            +
                            )
         | 
| 138 | 
            +
                        
         | 
| 139 | 
            +
                        if not ulid and not conditions:
         | 
| 140 | 
            +
                            raise CustomException(
         | 
| 141 | 
            +
                                ErrorCode.INVALID_INPUT,
         | 
| 142 | 
            +
                                detail="Either 'ulid' or 'conditions' must be provided.",
         | 
| 143 | 
            +
                                source_function="database.update_entity"
         | 
| 144 | 
            +
                            )
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                        # ulid로 조건 생성
         | 
| 147 | 
            +
                        if ulid:
         | 
| 148 | 
            +
                            conditions = {"ulid": ulid}
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                        conditions["is_deleted"] = False
         | 
| 151 | 
            +
             | 
| 152 | 
            +
                        return await self.repository.delete(
         | 
| 153 | 
            +
                            conditions=conditions
         | 
| 154 | 
            +
                        )
         | 
| 155 | 
            +
                    except CustomException as e:
         | 
| 156 | 
            +
                        raise e
         | 
| 157 | 
            +
                    except Exception as e:
         | 
| 158 | 
            +
                        raise CustomException(
         | 
| 159 | 
            +
                            ErrorCode.INTERNAL_ERROR,
         | 
| 160 | 
            +
                            detail=str(e),
         | 
| 161 | 
            +
                            source_function=f"{self.__class__.__name__}.delete",
         | 
| 162 | 
            +
                            original_error=e
         | 
| 163 | 
            +
                        )
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                #########################
         | 
| 166 | 
            +
                # 조회 및 검색 메서드 #
         | 
| 167 | 
            +
                #########################
         | 
| 168 | 
            +
                async def list(
         | 
| 169 | 
            +
                    self,
         | 
| 170 | 
            +
                    skip: int = 0,
         | 
| 171 | 
            +
                    limit: int = 100,
         | 
| 172 | 
            +
                    filters: List[Dict[str, Any]] | None = None,
         | 
| 173 | 
            +
                    model_name: str | None = None,
         | 
| 174 | 
            +
                    response_model: Any = None,
         | 
| 175 | 
            +
                    explicit_joins: Optional[List[Any]] = None,
         | 
| 176 | 
            +
                    loading_joins: Optional[List[Any]] = None
         | 
| 177 | 
            +
                ) -> List[Dict[str, Any]]:
         | 
| 178 | 
            +
                    try:
         | 
| 179 | 
            +
                        # 모델 이름을 통한 동적 처리
         | 
| 180 | 
            +
                        if model_name:
         | 
| 181 | 
            +
                            if model_name not in self.additional_models:
         | 
| 182 | 
            +
                                raise CustomException(
         | 
| 183 | 
            +
                                    ErrorCode.INVALID_REQUEST,
         | 
| 184 | 
            +
                                    detail=f"Model {model_name} not registered",
         | 
| 185 | 
            +
                                    source_function=f"{self.__class__.__name__}.list"
         | 
| 186 | 
            +
                                )
         | 
| 187 | 
            +
                            model = self.additional_models[model_name]
         | 
| 188 | 
            +
                            entities = await self.repository.list(
         | 
| 189 | 
            +
                                skip=skip,
         | 
| 190 | 
            +
                                limit=limit,
         | 
| 191 | 
            +
                                filters=filters,
         | 
| 192 | 
            +
                                model=model
         | 
| 193 | 
            +
                            )
         | 
| 194 | 
            +
                            return [process_response(entity, response_model) for entity in entities]
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                        entities = await self.repository.list(
         | 
| 197 | 
            +
                            skip=skip,
         | 
| 198 | 
            +
                            limit=limit,
         | 
| 199 | 
            +
                            filters=filters,
         | 
| 200 | 
            +
                            explicit_joins=explicit_joins,
         | 
| 201 | 
            +
                            loading_joins=loading_joins
         | 
| 202 | 
            +
                        )
         | 
| 203 | 
            +
                        return [process_response(entity, response_model) for entity in entities]
         | 
| 204 | 
            +
                    
         | 
| 205 | 
            +
                    except CustomException as e:
         | 
| 206 | 
            +
                        raise e
         | 
| 207 | 
            +
                    except Exception as e:
         | 
| 208 | 
            +
                        raise CustomException(
         | 
| 209 | 
            +
                            ErrorCode.INTERNAL_ERROR,
         | 
| 210 | 
            +
                            source_function=f"{self.__class__.__name__}.list",
         | 
| 211 | 
            +
                            original_error=e
         | 
| 212 | 
            +
                        )
         | 
| 213 | 
            +
                    
         | 
| 214 | 
            +
                async def get(
         | 
| 215 | 
            +
                    self,
         | 
| 216 | 
            +
                    ulid: str,
         | 
| 217 | 
            +
                    model_name: str | None = None,
         | 
| 218 | 
            +
                    response_model: Any = None,
         | 
| 219 | 
            +
                    conditions: Dict[str, Any] | None = None,
         | 
| 220 | 
            +
                    explicit_joins: Optional[List[Any]] = None,
         | 
| 221 | 
            +
                    loading_joins: Optional[List[Any]] = None
         | 
| 222 | 
            +
                ):
         | 
| 223 | 
            +
                    try:
         | 
| 224 | 
            +
                        if not ulid and not conditions:
         | 
| 225 | 
            +
                            raise CustomException(
         | 
| 226 | 
            +
                                ErrorCode.INVALID_INPUT,
         | 
| 227 | 
            +
                                detail="Either 'ulid' or 'conditions' must be provided.",
         | 
| 228 | 
            +
                                source_function="database.update_entity"
         | 
| 229 | 
            +
                            )
         | 
| 230 | 
            +
             | 
| 231 | 
            +
                        # ulid로 조건 생성
         | 
| 232 | 
            +
                        if ulid:
         | 
| 233 | 
            +
                            if not ULID.from_str(ulid):
         | 
| 234 | 
            +
                                raise CustomException(
         | 
| 235 | 
            +
                                    ErrorCode.VALIDATION_ERROR,
         | 
| 236 | 
            +
                                    detail=ulid,
         | 
| 237 | 
            +
                                    source_function=f"{self.__class__.__name__}.update"
         | 
| 238 | 
            +
                                )
         | 
| 239 | 
            +
                            
         | 
| 240 | 
            +
                            conditions = {"ulid": ulid}
         | 
| 241 | 
            +
                        
         | 
| 242 | 
            +
                        entity = await self.repository.get(
         | 
| 243 | 
            +
                            conditions=conditions,
         | 
| 244 | 
            +
                            explicit_joins=explicit_joins,
         | 
| 245 | 
            +
                            loading_joins=loading_joins
         | 
| 246 | 
            +
                        )
         | 
| 247 | 
            +
                        return process_response(entity, response_model)
         | 
| 248 | 
            +
             | 
| 249 | 
            +
                    except CustomException as e:
         | 
| 250 | 
            +
                        raise e
         | 
| 251 | 
            +
                    except Exception as e:
         | 
| 252 | 
            +
                        raise CustomException(
         | 
| 253 | 
            +
                            ErrorCode.INTERNAL_ERROR,
         | 
| 254 | 
            +
                            detail=str(e),
         | 
| 255 | 
            +
                            source_function=f"{self.__class__.__name__}.get",
         | 
| 256 | 
            +
                            original_error=e
         | 
| 257 | 
            +
                        )
         | 
| 258 | 
            +
             |