aiteamutils 0.2.59__tar.gz → 0.2.60__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiteamutils
3
- Version: 0.2.59
3
+ Version: 0.2.60
4
4
  Summary: AI Team Utilities
5
5
  Project-URL: Homepage, https://github.com/yourusername/aiteamutils
6
6
  Project-URL: Issues, https://github.com/yourusername/aiteamutils/issues
@@ -21,7 +21,6 @@ from .base_repository import BaseRepository
21
21
  from .validators import validate_with
22
22
  from .enums import ActivityType
23
23
  from .version import __version__
24
- from .dependencies import setup_dependencies, register_service
25
24
 
26
25
  __all__ = [
27
26
  # Base Models
@@ -32,10 +31,6 @@ __all__ = [
32
31
  # Database
33
32
  "DatabaseService",
34
33
 
35
- # Dependencies
36
- "setup_dependencies",
37
- "register_service",
38
-
39
34
  # Exceptions
40
35
  "CustomException",
41
36
  "ErrorCode",
@@ -0,0 +1,54 @@
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 list_entities
11
+
12
+ ModelType = TypeVar("ModelType", bound=DeclarativeBase)
13
+
14
+ class BaseRepository(Generic[ModelType]):
15
+ def __init__(self, session: AsyncSession, model: Type[ModelType]):
16
+ if session is None:
17
+ raise CustomException(
18
+ ErrorCode.DB_CONNECTION_ERROR,
19
+ detail="Database session is not set",
20
+ source_function=f"{self.__class__.__name__}.session"
21
+ )
22
+ if model is None:
23
+ raise CustomException(
24
+ ErrorCode.DB_CONNECTION_ERROR,
25
+ detail="Model is not set",
26
+ source_function=f"{self.__class__.__name__}.model"
27
+ )
28
+
29
+ self.session = session
30
+ self.model = model
31
+
32
+ @property
33
+ def session(self) -> AsyncSession:
34
+ return self._session
35
+
36
+ async def list(
37
+ self,
38
+ skip: int = 0,
39
+ limit: int = 100,
40
+ filters: Optional[Dict[str, Any]] = None,
41
+ joins: Optional[List[Any]] = None
42
+ ) -> List[ModelType]:
43
+ """
44
+ 엔티티 목록 조회.
45
+ """
46
+ # 기본 CRUD 작업 호출
47
+ return await list_entities(
48
+ session=self.db_session,
49
+ model=self.model,
50
+ skip=skip,
51
+ limit=limit,
52
+ filters=filters,
53
+ joins=joins,
54
+ )
@@ -0,0 +1,60 @@
1
+ #기본 라이브러리
2
+ from fastapi import Request
3
+ from typing import TypeVar, Generic, Type, Dict, Any, Union, List
4
+ from sqlalchemy.orm import DeclarativeBase
5
+ from sqlalchemy.ext.asyncio import AsyncSession
6
+ from datetime import datetime
7
+ #패키지 라이브러리
8
+ from .exceptions import ErrorCode, CustomException
9
+ from .base_repository import BaseRepository
10
+
11
+ ModelType = TypeVar("ModelType", bound=DeclarativeBase)
12
+
13
+ class BaseService(Generic[ModelType]):
14
+ ##################
15
+ # 1. 초기화 영역 #
16
+ ##################
17
+ def __init__(
18
+ self,
19
+ model: Type[ModelType],
20
+ repository: BaseRepository[ModelType],
21
+ db_session: AsyncSession
22
+ ):
23
+ self.model = model
24
+ self.repository = repository
25
+ self.db_session = db_session
26
+
27
+ async def list(
28
+ self,
29
+ skip: int = 0,
30
+ limit: int = 100,
31
+ filters: Dict[str, Any] | None = None,
32
+ search_params: Dict[str, Any] | None = None,
33
+ model_name: str | None = None,
34
+ ) -> List[Dict[str, Any]]:
35
+ try:
36
+ # 모델 이름을 통한 동적 처리
37
+ if model_name:
38
+ if model_name not in self.additional_models:
39
+ raise CustomException(
40
+ ErrorCode.INVALID_REQUEST,
41
+ detail=f"Model {model_name} not registered",
42
+ source_function=f"{self.__class__.__name__}.list"
43
+ )
44
+ model = self.additional_models[model_name]
45
+ return await self.repository.list(skip=skip, limit=limit, filters=filters, model=model)
46
+
47
+ return await self.repository.list(skip=skip, limit=limit, filters=filters)
48
+ except CustomException as e:
49
+ e.detail = f"Service list error for {self.repository.model.__tablename__}: {e.detail}"
50
+ e.source_function = f"{self.__class__.__name__}.list -> {e.source_function}"
51
+ raise e
52
+ except Exception as e:
53
+ raise CustomException(
54
+ ErrorCode.INTERNAL_ERROR,
55
+ detail=str(e),
56
+ source_function=f"{self.__class__.__name__}.list",
57
+ original_error=e
58
+ )
59
+
60
+
@@ -0,0 +1,48 @@
1
+ #기본 라이브러리
2
+ from typing import TypeVar, Generic, Type, Any, Dict, List, Optional
3
+ from sqlalchemy.ext.asyncio import AsyncSession
4
+ from sqlalchemy import select, and_
5
+ from sqlalchemy.orm import DeclarativeBase
6
+ from sqlalchemy.exc import SQLAlchemyError
7
+
8
+ ModelType = TypeVar("ModelType", bound=DeclarativeBase)
9
+
10
+ #패키지 라이브러리
11
+ from .exceptions import ErrorCode, CustomException
12
+
13
+ ##################
14
+ # 1. 쿼리 실행 #
15
+ ##################
16
+ async def list_entities(
17
+ session: AsyncSession,
18
+ model: Type[ModelType],
19
+ skip: int = 0,
20
+ limit: int = 100,
21
+ filters: Optional[Dict[str, Any]] = None,
22
+ joins: Optional[List[Any]] = None
23
+ ) -> List[Dict[str, Any]]:
24
+ try:
25
+ query = select(model)
26
+
27
+ # 필터 조건 적용
28
+ if filters:
29
+ conditions = [getattr(model, key) == value for key, value in filters.items()]
30
+ query = query.where(and_(*conditions))
31
+
32
+ # 조인 로딩 적용
33
+ if joins:
34
+ for join_option in joins:
35
+ query = query.options(join_option)
36
+
37
+ # 페이지네이션 적용
38
+ query = query.limit(limit).offset(skip)
39
+
40
+ result = await session.execute(query)
41
+ return result.scalars().unique().all()
42
+ except SQLAlchemyError as e:
43
+ raise CustomException(
44
+ ErrorCode.DB_READ_ERROR,
45
+ detail=f"{model.__name__}|{str(e)}",
46
+ source_function="database.list_entities",
47
+ original_error=e
48
+ )
@@ -0,0 +1,2 @@
1
+ """버전 정보"""
2
+ __version__ = "0.2.60"
@@ -1,208 +0,0 @@
1
- """기본 레포지토리 모듈."""
2
- from typing import TypeVar, Generic, Dict, Any, List, Optional, Type, Union
3
- from sqlalchemy.orm import DeclarativeBase, Load
4
- from sqlalchemy.exc import IntegrityError, SQLAlchemyError
5
- from sqlalchemy import select, or_, and_
6
- from .exceptions import CustomException, ErrorCode
7
- from sqlalchemy.orm import joinedload
8
- from sqlalchemy.sql import Select
9
- from fastapi import Request
10
- from sqlalchemy.ext.asyncio import AsyncSession
11
-
12
- ModelType = TypeVar("ModelType", bound=DeclarativeBase)
13
-
14
- class BaseRepository(Generic[ModelType]):
15
- ##################
16
- # 1. 초기화 영역 #
17
- ##################
18
- def __init__(self, session: AsyncSession, model: Type[ModelType]):
19
- """
20
- Args:
21
- session (AsyncSession): 데이터베이스 세션
22
- model (Type[ModelType]): 모델 클래스
23
- """
24
- self.session = session
25
- self.model = model
26
-
27
- @property
28
- def session(self):
29
- """현재 세션을 반환합니다."""
30
- if self._session is None:
31
- raise CustomException(
32
- ErrorCode.DB_CONNECTION_ERROR,
33
- detail="Database session is not set",
34
- source_function=f"{self.__class__.__name__}.session"
35
- )
36
- return self._session
37
-
38
- @session.setter
39
- def session(self, value):
40
- """세션을 설정합니다."""
41
- self._session = value
42
-
43
- #######################
44
- # 2. CRUD 작업 #
45
- #######################
46
- async def get(
47
- self,
48
- ulid: str
49
- ) -> Optional[Dict[str, Any]]:
50
- """ULID로 엔티티를 조회합니다."""
51
- try:
52
- stmt = select(self.model).filter_by(ulid=ulid, is_deleted=False)
53
- result = await self.session.execute(stmt)
54
- entity = result.scalars().unique().first()
55
-
56
- if not entity:
57
- raise CustomException(
58
- ErrorCode.DB_NO_RESULT,
59
- detail=f"{self.model.__tablename__}|ulid|{ulid}",
60
- source_function=f"{self.__class__.__name__}.get"
61
- )
62
- return entity
63
- except CustomException as e:
64
- e.detail = f"Repository error for {self.model.__tablename__}: {e.detail}"
65
- e.source_function = f"{self.__class__.__name__}.get -> {e.source_function}"
66
- raise e
67
- except SQLAlchemyError as e:
68
- raise CustomException(
69
- ErrorCode.DB_QUERY_ERROR,
70
- detail=f"Database error in {self.model.__tablename__}: {str(e)}",
71
- source_function=f"{self.__class__.__name__}.get",
72
- original_error=e
73
- )
74
-
75
- async def list(
76
- self,
77
- skip: int = 0,
78
- limit: int = 100,
79
- filters: Dict[str, Any] | None = None,
80
- search_params: Dict[str, Any] | None = None
81
- ) -> List[Any]:
82
- """엔티티 목록을 조회합니다."""
83
- try:
84
- stmt = select(self.model).where(self.model.is_deleted == False)
85
-
86
- # 필터 적용
87
- if filters:
88
- stmt = self._apply_filters(stmt, filters)
89
-
90
- # 검색 적용
91
- if search_params:
92
- stmt = self._apply_search_params(stmt, search_params)
93
-
94
- # 페이지네이션 적용
95
- stmt = stmt.limit(limit).offset(skip)
96
-
97
- result = await self.session.execute(stmt)
98
- return result.scalars().unique().all()
99
-
100
- except SQLAlchemyError as e:
101
- raise CustomException(
102
- ErrorCode.DB_QUERY_ERROR,
103
- detail=f"Unexpected repository list error in {self.model.__tablename__}: {str(e)}",
104
- source_function=f"{self.__class__.__name__}.list",
105
- original_error=e,
106
- )
107
-
108
- async def create(self, data: Dict[str, Any]) -> ModelType:
109
- """새로운 엔티티를 생성합니다."""
110
- try:
111
- entity = self.model(**data)
112
- self.session.add(entity)
113
- await self.session.flush()
114
- await self.session.refresh(entity)
115
- return entity
116
- except IntegrityError as e:
117
- await self.session.rollback()
118
- self._handle_integrity_error(e, "create", data)
119
- except SQLAlchemyError as e:
120
- await self.session.rollback()
121
- raise CustomException(
122
- ErrorCode.DB_CREATE_ERROR,
123
- detail=f"Database create error in {self.model.__tablename__}: {str(e)}",
124
- source_function=f"{self.__class__.__name__}.create",
125
- original_error=e
126
- )
127
-
128
- async def update(self, ulid: str, data: Dict[str, Any]) -> Optional[ModelType]:
129
- """기존 엔티티를 수정합니다."""
130
- try:
131
- stmt = select(self.model).filter_by(ulid=ulid, is_deleted=False)
132
- result = await self.session.execute(stmt)
133
- entity = result.scalars().first()
134
-
135
- if not entity:
136
- raise CustomException(
137
- ErrorCode.DB_NO_RESULT,
138
- detail=f"{self.model.__tablename__}|ulid|{ulid}",
139
- source_function=f"{self.__class__.__name__}.update"
140
- )
141
-
142
- for key, value in data.items():
143
- setattr(entity, key, value)
144
-
145
- await self.session.flush()
146
- await self.session.refresh(entity)
147
- return entity
148
-
149
- except IntegrityError as e:
150
- await self.session.rollback()
151
- self._handle_integrity_error(e, "update", data)
152
- except SQLAlchemyError as e:
153
- await self.session.rollback()
154
- raise CustomException(
155
- ErrorCode.DB_UPDATE_ERROR,
156
- detail=f"Database update error in {self.model.__tablename__}: {str(e)}",
157
- source_function=f"{self.__class__.__name__}.update",
158
- original_error=e
159
- )
160
-
161
- async def delete(self, ulid: str) -> bool:
162
- """엔티티를 소프트 삭제합니다."""
163
- try:
164
- stmt = select(self.model).filter_by(ulid=ulid, is_deleted=False)
165
- result = await self.session.execute(stmt)
166
- entity = result.scalars().first()
167
-
168
- if not entity:
169
- raise CustomException(
170
- ErrorCode.DB_NO_RESULT,
171
- detail=f"{self.model.__tablename__}|ulid|{ulid}",
172
- source_function=f"{self.__class__.__name__}.delete"
173
- )
174
-
175
- entity.is_deleted = True
176
- await self.session.flush()
177
- return True
178
-
179
- except SQLAlchemyError as e:
180
- await self.session.rollback()
181
- raise CustomException(
182
- ErrorCode.DB_DELETE_ERROR,
183
- detail=f"Database delete error in {self.model.__tablename__}: {str(e)}",
184
- source_function=f"{self.__class__.__name__}.delete",
185
- original_error=e
186
- )
187
-
188
- async def real_delete(self, ulid: str) -> bool:
189
- """엔티티를 실제로 삭제합니다."""
190
- try:
191
- stmt = select(self.model).filter_by(ulid=ulid)
192
- result = await self.session.execute(stmt)
193
- entity = result.scalars().first()
194
-
195
- if entity:
196
- await self.session.delete(entity)
197
- await self.session.flush()
198
- return True
199
- return False
200
-
201
- except SQLAlchemyError as e:
202
- await self.session.rollback()
203
- raise CustomException(
204
- ErrorCode.DB_DELETE_ERROR,
205
- detail=f"Database real delete error in {self.model.__tablename__}: {str(e)}",
206
- source_function=f"{self.__class__.__name__}.real_delete",
207
- original_error=e
208
- )
@@ -1,260 +0,0 @@
1
- """기본 서비스 모듈."""
2
- from datetime import datetime
3
- from typing import TypeVar, Generic, Dict, Any, List, Optional, Type
4
- from sqlalchemy.ext.asyncio import AsyncSession
5
- from sqlalchemy.orm import DeclarativeBase
6
- from fastapi import Request
7
-
8
- from .database import DatabaseService
9
- from .exceptions import CustomException, ErrorCode
10
- from .base_repository import BaseRepository
11
-
12
- ModelType = TypeVar("ModelType", bound=DeclarativeBase)
13
-
14
- class BaseService(Generic[ModelType]):
15
- def __init__(
16
- self,
17
- db: DatabaseService,
18
- repository: Optional[BaseRepository] = None,
19
- request: Optional[Request] = None
20
- ):
21
- """BaseService 초기화
22
-
23
- Args:
24
- db: 데이터베이스 서비스
25
- repository: 레포지토리 인스턴스 (선택)
26
- request: FastAPI 요청 객체 (선택)
27
- """
28
- self.db = db
29
- self.repository = repository
30
- self.request = request
31
- self.model = repository.model if repository else None
32
-
33
- def _process_response(self, entity: ModelType, response_model: Any = None) -> Dict[str, Any]:
34
- """응답 데이터를 처리합니다.
35
-
36
- Args:
37
- entity: 처리할 엔티티
38
- response_model: 응답 모델 클래스
39
-
40
- Returns:
41
- 처리된 데이터
42
- """
43
- if not entity:
44
- return None
45
-
46
- # 기본 데이터 변환
47
- result = {}
48
-
49
- # 테이블 컬럼 처리
50
- for column in entity.__table__.columns:
51
- value = getattr(entity, column.name)
52
- if isinstance(value, datetime):
53
- value = value.isoformat()
54
- result[column.name] = value
55
-
56
- # Relationship 처리 (이미 로드된 관계만)
57
- for relationship in entity.__mapper__.relationships:
58
- if relationship.key not in entity.__dict__:
59
- continue
60
-
61
- try:
62
- value = getattr(entity, relationship.key)
63
- if value is not None:
64
- if isinstance(value, list):
65
- result[relationship.key] = [
66
- self._process_response(item)
67
- for item in value
68
- ]
69
- else:
70
- result[relationship.key] = self._process_response(value)
71
- else:
72
- result[relationship.key] = None
73
- except Exception:
74
- result[relationship.key] = None
75
-
76
- # response_model이 있는 경우 필터링
77
- if response_model:
78
- # response_model에 없는 필드 제거
79
- keys_to_remove = [key for key in result if key not in response_model.model_fields]
80
- for key in keys_to_remove:
81
- result.pop(key)
82
- # 모델 검증
83
- return response_model(**result).model_dump()
84
-
85
- return result
86
-
87
- async def create(
88
- self,
89
- data: Dict[str, Any],
90
- response_model: Any = None
91
- ) -> Dict[str, Any]:
92
- """엔티티를 생성합니다.
93
-
94
- Args:
95
- data: 생성할 데이터
96
- response_model: 응답 모델 클래스
97
-
98
- Returns:
99
- 생성된 엔티티
100
-
101
- Raises:
102
- CustomException: 생성 실패 시
103
- """
104
- try:
105
- entity = await self.db.create_entity(self.model, data)
106
- return self._process_response(entity, response_model)
107
- except CustomException as e:
108
- raise e
109
- except Exception as e:
110
- raise CustomException(
111
- ErrorCode.DB_CREATE_ERROR,
112
- detail=str(e),
113
- source_function=f"{self.__class__.__name__}.create",
114
- original_error=e
115
- )
116
-
117
- async def get(
118
- self,
119
- ulid: str,
120
- response_model: Any = None
121
- ) -> Dict[str, Any]:
122
- """엔티티를 조회합니다.
123
-
124
- Args:
125
- ulid: 조회할 엔티티의 ULID
126
- response_model: 응답 모델 클래스
127
-
128
- Returns:
129
- 조회된 엔티티
130
-
131
- Raises:
132
- CustomException: 조회 실패 시
133
- """
134
- try:
135
- entity = await self.db.get_entity(self.model, {"ulid": ulid, "is_deleted": False})
136
- if not entity:
137
- raise CustomException(
138
- ErrorCode.NOT_FOUND,
139
- detail=f"{self.model.__tablename__}|ulid|{ulid}",
140
- source_function=f"{self.__class__.__name__}.get"
141
- )
142
- return self._process_response(entity, response_model)
143
- except CustomException as e:
144
- raise e
145
- except Exception as e:
146
- raise CustomException(
147
- ErrorCode.DB_QUERY_ERROR,
148
- detail=str(e),
149
- source_function=f"{self.__class__.__name__}.get",
150
- original_error=e
151
- )
152
-
153
- async def list(
154
- self,
155
- skip: int = 0,
156
- limit: int = 100,
157
- filters: Optional[Dict[str, Any]] = None,
158
- response_model: Any = None
159
- ) -> List[Dict[str, Any]]:
160
- """엔티티 목록을 조회합니다.
161
-
162
- Args:
163
- skip: 건너뛸 레코드 수
164
- limit: 조회할 최대 레코드 수
165
- filters: 필터 조건
166
- response_model: 응답 모델 클래스
167
-
168
- Returns:
169
- 엔티티 목록
170
-
171
- Raises:
172
- CustomException: 조회 실패 시
173
- """
174
- try:
175
- entities = await self.db.list_entities(
176
- self.model,
177
- filters=filters,
178
- skip=skip,
179
- limit=limit
180
- )
181
- return [self._process_response(entity, response_model) for entity in entities]
182
- except CustomException as e:
183
- raise e
184
- except Exception as e:
185
- raise CustomException(
186
- ErrorCode.DB_QUERY_ERROR,
187
- detail=str(e),
188
- source_function=f"{self.__class__.__name__}.list",
189
- original_error=e
190
- )
191
-
192
- async def update(
193
- self,
194
- ulid: str,
195
- data: Dict[str, Any],
196
- response_model: Any = None
197
- ) -> Dict[str, Any]:
198
- """엔티티를 수정합니다.
199
-
200
- Args:
201
- ulid: 수정할 엔티티의 ULID
202
- data: 수정할 데이터
203
- response_model: 응답 모델 클래스
204
-
205
- Returns:
206
- 수정된 엔티티
207
-
208
- Raises:
209
- CustomException: 수정 실패 시
210
- """
211
- try:
212
- entity = await self.db.get_entity(self.model, {"ulid": ulid, "is_deleted": False})
213
- if not entity:
214
- raise CustomException(
215
- ErrorCode.NOT_FOUND,
216
- detail=f"{self.model.__tablename__}|ulid|{ulid}",
217
- source_function=f"{self.__class__.__name__}.update"
218
- )
219
- updated = await self.db.update_entity(entity, data)
220
- return self._process_response(updated, response_model)
221
- except CustomException as e:
222
- raise e
223
- except Exception as e:
224
- raise CustomException(
225
- ErrorCode.DB_UPDATE_ERROR,
226
- detail=str(e),
227
- source_function=f"{self.__class__.__name__}.update",
228
- original_error=e
229
- )
230
-
231
- async def delete(self, ulid: str) -> bool:
232
- """엔티티를 삭제합니다.
233
-
234
- Args:
235
- ulid: 삭제할 엔티티의 ULID
236
-
237
- Returns:
238
- 삭제 성공 여부
239
-
240
- Raises:
241
- CustomException: 삭제 실패 시
242
- """
243
- try:
244
- entity = await self.db.get_entity(self.model, {"ulid": ulid, "is_deleted": False})
245
- if not entity:
246
- raise CustomException(
247
- ErrorCode.NOT_FOUND,
248
- detail=f"{self.model.__tablename__}|ulid|{ulid}",
249
- source_function=f"{self.__class__.__name__}.delete"
250
- )
251
- return await self.db.delete_entity(entity)
252
- except CustomException as e:
253
- raise e
254
- except Exception as e:
255
- raise CustomException(
256
- ErrorCode.DB_DELETE_ERROR,
257
- detail=str(e),
258
- source_function=f"{self.__class__.__name__}.delete",
259
- original_error=e
260
- )
@@ -1,207 +0,0 @@
1
- """데이터베이스 유틸리티 모듈."""
2
- from typing import Any, Dict, Optional, Type, List, Union
3
- from sqlalchemy import select, and_
4
- from sqlalchemy.ext.asyncio import AsyncSession
5
- from sqlalchemy.exc import IntegrityError, SQLAlchemyError
6
-
7
- from .exceptions import ErrorCode, CustomException
8
- from .base_model import Base
9
-
10
- class DatabaseService:
11
- def __init__(self, session: AsyncSession):
12
- """DatabaseService 초기화
13
-
14
- Args:
15
- session (AsyncSession): 외부에서 주입받은 데이터베이스 세션
16
- """
17
- self._session = session
18
-
19
- @property
20
- def session(self) -> AsyncSession:
21
- """현재 세션을 반환합니다."""
22
- if self._session is None:
23
- raise CustomException(
24
- ErrorCode.DB_CONNECTION_ERROR,
25
- detail="session",
26
- source_function="DatabaseService.session"
27
- )
28
- return self._session
29
-
30
- async def create_entity(
31
- self,
32
- model: Type[Base],
33
- entity_data: Dict[str, Any]
34
- ) -> Any:
35
- """엔티티를 생성합니다.
36
-
37
- Args:
38
- model: 모델 클래스
39
- entity_data: 생성할 엔티티 데이터
40
-
41
- Returns:
42
- 생성된 엔티티
43
-
44
- Raises:
45
- CustomException: 엔티티 생성 실패 시
46
- """
47
- try:
48
- entity = model(**entity_data)
49
- self.session.add(entity)
50
- await self.session.flush()
51
- await self.session.refresh(entity)
52
- return entity
53
- except IntegrityError as e:
54
- await self.session.rollback()
55
- raise CustomException(
56
- ErrorCode.DB_INTEGRITY_ERROR,
57
- detail=str(e),
58
- source_function="DatabaseService.create_entity",
59
- original_error=e
60
- )
61
- except Exception as e:
62
- await self.session.rollback()
63
- raise CustomException(
64
- ErrorCode.DB_CREATE_ERROR,
65
- detail=str(e),
66
- source_function="DatabaseService.create_entity",
67
- original_error=e
68
- )
69
-
70
- async def get_entity(
71
- self,
72
- model: Type[Base],
73
- filters: Dict[str, Any]
74
- ) -> Optional[Any]:
75
- """필터 조건으로 엔티티를 조회합니다.
76
-
77
- Args:
78
- model: 모델 클래스
79
- filters: 필터 조건
80
-
81
- Returns:
82
- 조회된 엔티티 또는 None
83
-
84
- Raises:
85
- CustomException: 조회 실패 시
86
- """
87
- try:
88
- stmt = select(model).filter_by(**filters)
89
- result = await self.session.execute(stmt)
90
- return result.scalars().first()
91
- except Exception as e:
92
- raise CustomException(
93
- ErrorCode.DB_QUERY_ERROR,
94
- detail=str(e),
95
- source_function="DatabaseService.get_entity",
96
- original_error=e
97
- )
98
-
99
- async def list_entities(
100
- self,
101
- model: Type[Base],
102
- filters: Optional[Dict[str, Any]] = None,
103
- skip: int = 0,
104
- limit: int = 100
105
- ) -> List[Any]:
106
- """엔티티 목록을 조회합니다.
107
-
108
- Args:
109
- model: 모델 클래스
110
- filters: 필터 조건
111
- skip: 건너뛸 레코드 수
112
- limit: 조회할 최대 레코드 수
113
-
114
- Returns:
115
- 엔티티 목록
116
-
117
- Raises:
118
- CustomException: 조회 실패 시
119
- """
120
- try:
121
- stmt = select(model)
122
- if filters:
123
- stmt = stmt.filter_by(**filters)
124
- stmt = stmt.offset(skip).limit(limit)
125
- result = await self.session.execute(stmt)
126
- return result.scalars().all()
127
- except Exception as e:
128
- raise CustomException(
129
- ErrorCode.DB_QUERY_ERROR,
130
- detail=str(e),
131
- source_function="DatabaseService.list_entities",
132
- original_error=e
133
- )
134
-
135
- async def update_entity(
136
- self,
137
- entity: Base,
138
- update_data: Dict[str, Any]
139
- ) -> Any:
140
- """엔티티를 수정합니다.
141
-
142
- Args:
143
- entity: 수정할 엔티티
144
- update_data: 수정할 데이터
145
-
146
- Returns:
147
- 수정된 엔티티
148
-
149
- Raises:
150
- CustomException: 수정 실패 시
151
- """
152
- try:
153
- for key, value in update_data.items():
154
- setattr(entity, key, value)
155
- await self.session.flush()
156
- await self.session.refresh(entity)
157
- return entity
158
- except IntegrityError as e:
159
- await self.session.rollback()
160
- raise CustomException(
161
- ErrorCode.DB_INTEGRITY_ERROR,
162
- detail=str(e),
163
- source_function="DatabaseService.update_entity",
164
- original_error=e
165
- )
166
- except Exception as e:
167
- await self.session.rollback()
168
- raise CustomException(
169
- ErrorCode.DB_UPDATE_ERROR,
170
- detail=str(e),
171
- source_function="DatabaseService.update_entity",
172
- original_error=e
173
- )
174
-
175
- async def delete_entity(
176
- self,
177
- entity: Base,
178
- soft_delete: bool = True
179
- ) -> bool:
180
- """엔티티를 삭제합니다.
181
-
182
- Args:
183
- entity: 삭제할 엔티티
184
- soft_delete: 소프트 삭제 여부
185
-
186
- Returns:
187
- 삭제 성공 여부
188
-
189
- Raises:
190
- CustomException: 삭제 실패 시
191
- """
192
- try:
193
- if soft_delete:
194
- entity.is_deleted = True
195
- await self.session.flush()
196
- else:
197
- await self.session.delete(entity)
198
- await self.session.flush()
199
- return True
200
- except Exception as e:
201
- await self.session.rollback()
202
- raise CustomException(
203
- ErrorCode.DB_DELETE_ERROR,
204
- detail=str(e),
205
- source_function="DatabaseService.delete_entity",
206
- original_error=e
207
- )
@@ -1,138 +0,0 @@
1
- """의존성 관리 모듈."""
2
- from typing import Type, TypeVar, Dict, Any, Optional, Callable, List, AsyncGenerator
3
- from fastapi import Request, Depends
4
- from sqlalchemy.ext.asyncio import AsyncSession
5
-
6
- from .exceptions import CustomException, ErrorCode
7
- from .base_service import BaseService
8
- from .base_repository import BaseRepository
9
- from .database import DatabaseService
10
-
11
- T = TypeVar("T", bound=BaseService)
12
- R = TypeVar("R", bound=BaseRepository)
13
-
14
- _service_registry: Dict[str, Dict[str, Any]] = {}
15
- _session_provider = None
16
-
17
- __all__ = [
18
- "setup_dependencies",
19
- "register_service",
20
- "get_service"
21
- ]
22
-
23
- def setup_dependencies(session_provider: Callable[[], AsyncGenerator[AsyncSession, None]]) -> None:
24
- """의존성 설정을 초기화합니다.
25
-
26
- Args:
27
- session_provider: 데이터베이스 세션을 제공하는 함수
28
- """
29
- global _session_provider
30
- _session_provider = session_provider
31
-
32
- def register_service(
33
- service_class: Type[T],
34
- repository_class: Optional[Type[R]] = None,
35
- **kwargs
36
- ) -> None:
37
- """서비스를 등록합니다.
38
-
39
- Args:
40
- service_class: 서비스 클래스
41
- repository_class: 저장소 클래스 (선택)
42
- **kwargs: 추가 의존성
43
- """
44
- if _session_provider is None:
45
- raise CustomException(
46
- ErrorCode.INTERNAL_ERROR,
47
- detail="Dependencies not initialized",
48
- source_function="dependencies.register_service"
49
- )
50
-
51
- service_name = service_class.__name__
52
- _service_registry[service_name] = {
53
- "service_class": service_class,
54
- "repository_class": repository_class,
55
- "dependencies": kwargs
56
- }
57
-
58
- async def _get_service(
59
- service_name: str,
60
- session: AsyncSession,
61
- request: Request
62
- ) -> BaseService:
63
- """서비스 인스턴스를 생성합니다.
64
-
65
- Args:
66
- service_name: 서비스 이름
67
- session: 데이터베이스 세션
68
- request: FastAPI 요청 객체
69
-
70
- Returns:
71
- BaseService: 서비스 인스턴스
72
-
73
- Raises:
74
- CustomException: 서비스 생성 실패 시
75
- """
76
- try:
77
- service_info = _service_registry.get(service_name)
78
- if not service_info:
79
- raise CustomException(
80
- ErrorCode.SERVICE_NOT_FOUND,
81
- detail=service_name,
82
- source_function="dependencies._get_service"
83
- )
84
-
85
- service_class = service_info["service_class"]
86
- repository_class = service_info["repository_class"]
87
- dependencies = service_info["dependencies"]
88
-
89
- # 데이터베이스 서비스 생성
90
- db_service = DatabaseService(session=session)
91
-
92
- # 저장소 인스턴스 생성
93
- repository = None
94
- if repository_class:
95
- repository = repository_class(session=session)
96
-
97
- # 서비스 인스턴스 생성
98
- service = service_class(
99
- db=db_service,
100
- repository=repository,
101
- request=request,
102
- **dependencies
103
- )
104
-
105
- return service
106
-
107
- except CustomException as e:
108
- raise e
109
- except Exception as e:
110
- raise CustomException(
111
- ErrorCode.INTERNAL_ERROR,
112
- detail=str(e),
113
- source_function="dependencies._get_service",
114
- original_error=e
115
- )
116
-
117
- def get_service(service_name: str) -> Callable:
118
- """서비스 의존성을 반환합니다.
119
-
120
- Args:
121
- service_name: 서비스 이름
122
-
123
- Returns:
124
- Callable: 서비스 의존성 함수
125
- """
126
- if _session_provider is None:
127
- raise CustomException(
128
- ErrorCode.INTERNAL_ERROR,
129
- detail="Dependencies not initialized",
130
- source_function="dependencies.get_service"
131
- )
132
-
133
- async def _get_service_dependency(
134
- request: Request,
135
- session: AsyncSession = Depends(_session_provider)
136
- ) -> BaseService:
137
- return await _get_service(service_name, session, request)
138
- return _get_service_dependency
@@ -1,2 +0,0 @@
1
- """버전 정보"""
2
- __version__ = "0.2.59"
File without changes
File without changes
File without changes
File without changes