aiteamutils 0.2.58__py3-none-any.whl → 0.2.60__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
aiteamutils/database.py CHANGED
@@ -1,207 +1,48 @@
1
- """데이터베이스 유틸리티 모듈."""
2
- from typing import Any, Dict, Optional, Type, List, Union
3
- from sqlalchemy import select, and_
1
+ #기본 라이브러리
2
+ from typing import TypeVar, Generic, Type, Any, Dict, List, Optional
4
3
  from sqlalchemy.ext.asyncio import AsyncSession
5
- from sqlalchemy.exc import IntegrityError, SQLAlchemyError
4
+ from sqlalchemy import select, and_
5
+ from sqlalchemy.orm import DeclarativeBase
6
+ from sqlalchemy.exc import SQLAlchemyError
6
7
 
7
- from .exceptions import ErrorCode, CustomException
8
- from .base_model import Base
8
+ ModelType = TypeVar("ModelType", bound=DeclarativeBase)
9
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
10
+ #패키지 라이브러리
11
+ from .exceptions import ErrorCode, CustomException
29
12
 
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
- )
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)
69
26
 
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
- )
27
+ # 필터 조건 적용
28
+ if filters:
29
+ conditions = [getattr(model, key) == value for key, value in filters.items()]
30
+ query = query.where(and_(*conditions))
98
31
 
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
- )
32
+ # 조인 로딩 적용
33
+ if joins:
34
+ for join_option in joins:
35
+ query = query.options(join_option)
134
36
 
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
- )
37
+ # 페이지네이션 적용
38
+ query = query.limit(limit).offset(skip)
174
39
 
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
- )
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
+ )
aiteamutils/version.py CHANGED
@@ -1,2 +1,2 @@
1
1
  """버전 정보"""
2
- __version__ = "0.2.58"
2
+ __version__ = "0.2.60"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiteamutils
3
- Version: 0.2.58
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
@@ -0,0 +1,15 @@
1
+ aiteamutils/__init__.py,sha256=h7iFifWvRlaComsk4VPapRr16YhD19Kp1LuBzLHqdgQ,1170
2
+ aiteamutils/base_model.py,sha256=ODEnjvUVoxQ1RPCfq8-uZTfTADIA4c7Z3E6G4EVsSX0,2708
3
+ aiteamutils/base_repository.py,sha256=ir2ftophKKd_9BQ9B0LbszMqUyFeWkQ1MmFF6WBLcWc,1682
4
+ aiteamutils/base_service.py,sha256=Ncomi5yBUL4oHrMx_J1yUKVrUIYo6YmnYJWAWOIwQx8,2217
5
+ aiteamutils/cache.py,sha256=07xBGlgAwOTAdY5mnMOQJ5EBxVwe8glVD7DkGEkxCtw,1373
6
+ aiteamutils/config.py,sha256=YdalpJb70-txhGJAS4aaKglEZAFVWgfzw5BXSWpkUz4,3232
7
+ aiteamutils/database.py,sha256=MtmrX_pDzKFQM-P3OAfm2mALvhRg-v5JWtGBoiegeU0,1484
8
+ aiteamutils/enums.py,sha256=ipZi6k_QD5-3QV7Yzv7bnL0MjDz-vqfO9I5L77biMKs,632
9
+ aiteamutils/exceptions.py,sha256=_lKWXq_ujNj41xN6LDE149PwsecAP7lgYWbOBbLOntg,15368
10
+ aiteamutils/security.py,sha256=xFVrjttxwXB1TTjqgRQQgQJQohQBT28vuW8FVLjvi-M,10103
11
+ aiteamutils/validators.py,sha256=3N245cZFjgwtW_KzjESkizx5BBUDaJLbbxfNO4WOFZ0,7764
12
+ aiteamutils/version.py,sha256=W0y4-03EvxLX9F9qSLA2iCovEsiyhT86gHqCOTRNhGU,42
13
+ aiteamutils-0.2.60.dist-info/METADATA,sha256=Gu1bs923MYkC_xKFhMKgS8PrSsK2ne2UJRUxBBeflRI,1718
14
+ aiteamutils-0.2.60.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
15
+ aiteamutils-0.2.60.dist-info/RECORD,,
@@ -1,227 +0,0 @@
1
- """의존성 관리 모듈."""
2
- from typing import Type, TypeVar, Dict, Any, Optional, Callable, List, AsyncGenerator
3
- from fastapi import Request, Depends, HTTPException, status
4
- from sqlalchemy.ext.asyncio import AsyncSession
5
- from jose import jwt, JWTError
6
- import logging
7
-
8
- from .exceptions import CustomException, ErrorCode
9
- from .config import get_settings
10
- from .base_service import BaseService
11
- from .base_repository import BaseRepository
12
- from .database import DatabaseService
13
-
14
- T = TypeVar("T", bound=BaseService)
15
- R = TypeVar("R", bound=BaseRepository)
16
-
17
- _service_registry: Dict[str, Dict[str, Any]] = {}
18
- _session_provider = None
19
-
20
- __all__ = [
21
- "setup_dependencies",
22
- "register_service",
23
- "get_service",
24
- "get_current_user"
25
- ]
26
-
27
- def setup_dependencies(session_provider: Callable[[], AsyncGenerator[AsyncSession, None]]) -> None:
28
- """의존성 설정을 초기화합니다.
29
-
30
- Args:
31
- session_provider: 데이터베이스 세션을 제공하는 함수
32
- """
33
- global _session_provider
34
- _session_provider = session_provider
35
-
36
- def register_service(
37
- service_class: Type[T],
38
- repository_class: Optional[Type[R]] = None,
39
- **kwargs
40
- ) -> None:
41
- """서비스를 등록합니다.
42
-
43
- Args:
44
- service_class: 서비스 클래스
45
- repository_class: 저장소 클래스 (선택)
46
- **kwargs: 추가 의존성
47
- """
48
- if _session_provider is None:
49
- raise CustomException(
50
- ErrorCode.INTERNAL_ERROR,
51
- detail="Dependencies not initialized",
52
- source_function="dependencies.register_service"
53
- )
54
-
55
- service_name = service_class.__name__
56
- _service_registry[service_name] = {
57
- "service_class": service_class,
58
- "repository_class": repository_class,
59
- "dependencies": kwargs
60
- }
61
-
62
- async def _get_service(
63
- service_name: str,
64
- session: AsyncSession,
65
- request: Request
66
- ) -> BaseService:
67
- """서비스 인스턴스를 생성합니다.
68
-
69
- Args:
70
- service_name: 서비스 이름
71
- session: 데이터베이스 세션
72
- request: FastAPI 요청 객체
73
-
74
- Returns:
75
- BaseService: 서비스 인스턴스
76
-
77
- Raises:
78
- CustomException: 서비스 생성 실패 시
79
- """
80
- try:
81
- service_info = _service_registry.get(service_name)
82
- if not service_info:
83
- raise CustomException(
84
- ErrorCode.SERVICE_NOT_FOUND,
85
- detail=service_name,
86
- source_function="dependencies._get_service"
87
- )
88
-
89
- service_class = service_info["service_class"]
90
- repository_class = service_info["repository_class"]
91
- dependencies = service_info["dependencies"]
92
-
93
- # 데이터베이스 서비스 생성
94
- db_service = DatabaseService(session=session)
95
-
96
- # 저장소 인스턴스 생성
97
- repository = None
98
- if repository_class:
99
- repository = repository_class(session=session)
100
-
101
- # 서비스 인스턴스 생성
102
- service = service_class(
103
- db=db_service,
104
- repository=repository,
105
- request=request,
106
- **dependencies
107
- )
108
-
109
- return service
110
-
111
- except CustomException as e:
112
- raise e
113
- except Exception as e:
114
- raise CustomException(
115
- ErrorCode.INTERNAL_ERROR,
116
- detail=str(e),
117
- source_function="dependencies._get_service",
118
- original_error=e
119
- )
120
-
121
- def get_service(service_name: str) -> Callable:
122
- """서비스 의존성을 반환합니다.
123
-
124
- Args:
125
- service_name: 서비스 이름
126
-
127
- Returns:
128
- Callable: 서비스 의존성 함수
129
- """
130
- if _session_provider is None:
131
- raise CustomException(
132
- ErrorCode.INTERNAL_ERROR,
133
- detail="Dependencies not initialized",
134
- source_function="dependencies.get_service"
135
- )
136
-
137
- async def _get_service_dependency(
138
- request: Request,
139
- session: AsyncSession = Depends(_session_provider)
140
- ) -> BaseService:
141
- return await _get_service(service_name, session, request)
142
- return _get_service_dependency
143
-
144
- async def get_current_user(
145
- request: Request,
146
- session: AsyncSession,
147
- auth_service: BaseService = Depends(get_service("AuthService"))
148
- ) -> Dict[str, Any]:
149
- """현재 사용자 정보를 반환합니다.
150
-
151
- Args:
152
- request: FastAPI 요청 객체
153
- session: 데이터베이스 세션
154
- auth_service: 인증 서비스
155
-
156
- Returns:
157
- Dict[str, Any]: 사용자 정보
158
-
159
- Raises:
160
- HTTPException: 인증 실패 시
161
- """
162
- settings = get_settings()
163
-
164
- # Authorization 헤더 검증
165
- authorization = request.headers.get("Authorization")
166
- if not authorization or not authorization.startswith("Bearer "):
167
- raise HTTPException(
168
- status_code=status.HTTP_401_UNAUTHORIZED,
169
- detail="Not authenticated",
170
- headers={"WWW-Authenticate": "Bearer"}
171
- )
172
-
173
- token = authorization.split(" ")[1]
174
-
175
- try:
176
- # JWT 토큰 디코딩
177
- payload = jwt.decode(
178
- token,
179
- settings.jwt_secret,
180
- algorithms=[settings.jwt_algorithm],
181
- issuer=settings.token_issuer,
182
- audience=settings.token_audience
183
- )
184
-
185
- # 토큰 타입 검증
186
- token_type = payload.get("token_type")
187
- if token_type != "access":
188
- raise HTTPException(
189
- status_code=status.HTTP_401_UNAUTHORIZED,
190
- detail="Invalid token type",
191
- headers={"WWW-Authenticate": "Bearer"}
192
- )
193
-
194
- # 사용자 조회
195
- user_ulid = payload.get("user_ulid")
196
- if not user_ulid:
197
- raise HTTPException(
198
- status_code=status.HTTP_401_UNAUTHORIZED,
199
- detail="Invalid token payload",
200
- headers={"WWW-Authenticate": "Bearer"}
201
- )
202
-
203
- user = await auth_service.get_by_ulid(user_ulid)
204
- if not user:
205
- raise HTTPException(
206
- status_code=status.HTTP_401_UNAUTHORIZED,
207
- detail="User not found",
208
- headers={"WWW-Authenticate": "Bearer"}
209
- )
210
-
211
- return user
212
-
213
- except JWTError as e:
214
- raise HTTPException(
215
- status_code=status.HTTP_401_UNAUTHORIZED,
216
- detail=str(e),
217
- headers={"WWW-Authenticate": "Bearer"}
218
- )
219
- except HTTPException as e:
220
- raise e
221
- except Exception as e:
222
- logging.error(f"Error in get_current_user: {str(e)}")
223
- raise HTTPException(
224
- status_code=status.HTTP_401_UNAUTHORIZED,
225
- detail="Authentication failed",
226
- headers={"WWW-Authenticate": "Bearer"}
227
- )
@@ -1,16 +0,0 @@
1
- aiteamutils/__init__.py,sha256=U9UEyU0AqpLZzbPJiBvwDEh-s2405y1IeWZ0zCqFSl0,1307
2
- aiteamutils/base_model.py,sha256=ODEnjvUVoxQ1RPCfq8-uZTfTADIA4c7Z3E6G4EVsSX0,2708
3
- aiteamutils/base_repository.py,sha256=vqsundoN0h7FVvgqTBEnnJNMcFpvMK0s_nxBWdIYg-U,7846
4
- aiteamutils/base_service.py,sha256=s2AcA-6_ogOQKgt2xf_3AG2s6tqBceU4nJoXO1II7S8,24588
5
- aiteamutils/cache.py,sha256=07xBGlgAwOTAdY5mnMOQJ5EBxVwe8glVD7DkGEkxCtw,1373
6
- aiteamutils/config.py,sha256=YdalpJb70-txhGJAS4aaKglEZAFVWgfzw5BXSWpkUz4,3232
7
- aiteamutils/database.py,sha256=x0x5gnSyGfwo_klL9O65RnGOQID6c9tH2miwFveVyoE,6326
8
- aiteamutils/dependencies.py,sha256=UjHGgee4qKxPwHJnfMZ3YKB7m7UQNIvKNr7IdOx590c,6899
9
- aiteamutils/enums.py,sha256=ipZi6k_QD5-3QV7Yzv7bnL0MjDz-vqfO9I5L77biMKs,632
10
- aiteamutils/exceptions.py,sha256=_lKWXq_ujNj41xN6LDE149PwsecAP7lgYWbOBbLOntg,15368
11
- aiteamutils/security.py,sha256=xFVrjttxwXB1TTjqgRQQgQJQohQBT28vuW8FVLjvi-M,10103
12
- aiteamutils/validators.py,sha256=3N245cZFjgwtW_KzjESkizx5BBUDaJLbbxfNO4WOFZ0,7764
13
- aiteamutils/version.py,sha256=57HkAV8pVrmV03eTVINw5qTQURkXwGPl2H--uJC4OWs,42
14
- aiteamutils-0.2.58.dist-info/METADATA,sha256=yULGE3mtpwvWXzHDczGJfqu52urFzknJhm5V9PgCOGc,1718
15
- aiteamutils-0.2.58.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
- aiteamutils-0.2.58.dist-info/RECORD,,