aiteamutils 0.2.136__py3-none-any.whl → 0.2.138__py3-none-any.whl
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/base_repository.py +2 -2
- aiteamutils/base_service.py +66 -32
- aiteamutils/database.py +2 -2
- aiteamutils/validators.py +2 -63
- aiteamutils/version.py +1 -1
- {aiteamutils-0.2.136.dist-info → aiteamutils-0.2.138.dist-info}/METADATA +1 -1
- {aiteamutils-0.2.136.dist-info → aiteamutils-0.2.138.dist-info}/RECORD +8 -8
- {aiteamutils-0.2.136.dist-info → aiteamutils-0.2.138.dist-info}/WHEEL +0 -0
aiteamutils/base_repository.py
CHANGED
@@ -32,7 +32,7 @@ class BaseRepository(Generic[ModelType]):
|
|
32
32
|
raise CustomException(
|
33
33
|
ErrorCode.DB_CONNECTION_ERROR,
|
34
34
|
detail="Session cannot be None",
|
35
|
-
source_function=f"{self.__class__.__name__}.session"
|
35
|
+
source_function=f"base_repository.{self.__class__.__name__}.session"
|
36
36
|
)
|
37
37
|
self._session = value
|
38
38
|
|
@@ -57,7 +57,7 @@ class BaseRepository(Generic[ModelType]):
|
|
57
57
|
raise CustomException(
|
58
58
|
ErrorCode.INTERNAL_ERROR,
|
59
59
|
detail=str(e),
|
60
|
-
source_function=f"{self.__class__.__name__}.create",
|
60
|
+
source_function=f"base_repository.{self.__class__.__name__}.create",
|
61
61
|
original_error=e
|
62
62
|
)
|
63
63
|
|
aiteamutils/base_service.py
CHANGED
@@ -61,10 +61,35 @@ class BaseService(Generic[ModelType]):
|
|
61
61
|
raise CustomException(
|
62
62
|
ErrorCode.FORBIDDEN,
|
63
63
|
detail=f"{role_permission}",
|
64
|
-
source_function=f"{self.__class__.__name__}.create"
|
64
|
+
source_function=f"base_service.{self.__class__.__name__}.create.permission_result"
|
65
65
|
)
|
66
66
|
|
67
67
|
try:
|
68
|
+
# 파일 데이터 분리
|
69
|
+
entity_data_copy = entity_data.copy()
|
70
|
+
separated_files = {}
|
71
|
+
|
72
|
+
# extra_data 내의 파일 필드 분리
|
73
|
+
if 'extra_data' in entity_data_copy and isinstance(entity_data_copy['extra_data'], dict):
|
74
|
+
extra_data = entity_data_copy['extra_data'].copy()
|
75
|
+
file_fields = {k: v for k, v in extra_data.items() if k.endswith('_files')}
|
76
|
+
|
77
|
+
if file_fields and not storage_dir:
|
78
|
+
raise CustomException(
|
79
|
+
ErrorCode.INVALID_INPUT,
|
80
|
+
detail="storage_dir is required for file upload",
|
81
|
+
source_function=f"base_service.{self.__class__.__name__}.create.file_fields"
|
82
|
+
)
|
83
|
+
|
84
|
+
# 파일 필드 분리 및 제거
|
85
|
+
for field_name, files in file_fields.items():
|
86
|
+
if files:
|
87
|
+
separated_files[field_name] = files
|
88
|
+
# extra_data에서 파일 필드 제거
|
89
|
+
del extra_data[field_name]
|
90
|
+
|
91
|
+
entity_data_copy['extra_data'] = extra_data
|
92
|
+
|
68
93
|
async with self.db_session.begin():
|
69
94
|
# 고유 검사 수행
|
70
95
|
if unique_check:
|
@@ -73,35 +98,44 @@ class BaseService(Generic[ModelType]):
|
|
73
98
|
if fk_check:
|
74
99
|
await validate_unique_fields(self.db_session, fk_check, find_value=False)
|
75
100
|
|
76
|
-
#
|
77
|
-
file_keys = [key for key in entity_data.keys() if key.endswith('_files')]
|
78
|
-
file_infos = {}
|
79
|
-
|
80
|
-
if file_keys and not storage_dir:
|
81
|
-
raise CustomException(
|
82
|
-
ErrorCode.INVALID_INPUT,
|
83
|
-
detail="storage_dir is required for file upload",
|
84
|
-
source_function=f"{self.__class__.__name__}.create"
|
85
|
-
)
|
86
|
-
|
87
|
-
# 먼저 엔티티를 생성하여 ULID를 얻음
|
101
|
+
# 엔티티 생성
|
88
102
|
result = await self.repository.create(
|
89
|
-
entity_data=
|
103
|
+
entity_data=entity_data_copy,
|
90
104
|
exclude_entities=exclude_entities
|
91
105
|
)
|
92
106
|
|
93
|
-
# 파일 처리
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
files=
|
107
|
+
# 파일 처리 및 저장
|
108
|
+
file_infos = {}
|
109
|
+
if separated_files:
|
110
|
+
from .files import FileHandler
|
111
|
+
for field_name, files in separated_files.items():
|
112
|
+
saved_files = await FileHandler.save_files(
|
113
|
+
files=files,
|
100
114
|
storage_dir=storage_dir,
|
101
115
|
entity_name=self.model.__tablename__,
|
102
116
|
entity_ulid=result.ulid,
|
103
117
|
db_session=self.db_session
|
104
118
|
)
|
119
|
+
file_infos[field_name] = saved_files
|
120
|
+
|
121
|
+
# extra_data 업데이트
|
122
|
+
if not hasattr(result, 'extra_data'):
|
123
|
+
result.extra_data = {}
|
124
|
+
if not result.extra_data:
|
125
|
+
result.extra_data = {}
|
126
|
+
|
127
|
+
result.extra_data[field_name] = [
|
128
|
+
{
|
129
|
+
'original_name': f['original_name'],
|
130
|
+
'storage_path': f['storage_path'],
|
131
|
+
'mime_type': f['mime_type'],
|
132
|
+
'size': f['size'],
|
133
|
+
'checksum': f['checksum']
|
134
|
+
} for f in saved_files
|
135
|
+
]
|
136
|
+
|
137
|
+
# extra_data 업데이트된 엔티티 저장
|
138
|
+
await self.db_session.flush()
|
105
139
|
|
106
140
|
# 결과 반환
|
107
141
|
if response_model:
|
@@ -126,7 +160,7 @@ class BaseService(Generic[ModelType]):
|
|
126
160
|
raise CustomException(
|
127
161
|
ErrorCode.INTERNAL_ERROR,
|
128
162
|
detail=str(e),
|
129
|
-
source_function=f"{self.__class__.__name__}.create",
|
163
|
+
source_function=f"base_service.{self.__class__.__name__}.create",
|
130
164
|
original_error=e
|
131
165
|
)
|
132
166
|
|
@@ -153,7 +187,7 @@ class BaseService(Generic[ModelType]):
|
|
153
187
|
raise CustomException(
|
154
188
|
ErrorCode.INVALID_INPUT,
|
155
189
|
detail="Either 'ulid' or 'conditions' must be provided.",
|
156
|
-
source_function="
|
190
|
+
source_function=f"base_service.{self.__class__.__name__}.update.ulid_or_conditions"
|
157
191
|
)
|
158
192
|
|
159
193
|
# ulid로 조건 생성
|
@@ -162,7 +196,7 @@ class BaseService(Generic[ModelType]):
|
|
162
196
|
raise CustomException(
|
163
197
|
ErrorCode.VALIDATION_ERROR,
|
164
198
|
detail=ulid,
|
165
|
-
source_function=f"{self.__class__.__name__}.update"
|
199
|
+
source_function=f"base_service.{self.__class__.__name__}.update.ulid_or_conditions"
|
166
200
|
)
|
167
201
|
|
168
202
|
conditions = {"ulid": ulid}
|
@@ -183,7 +217,7 @@ class BaseService(Generic[ModelType]):
|
|
183
217
|
raise CustomException(
|
184
218
|
ErrorCode.INTERNAL_ERROR,
|
185
219
|
detail=str(e),
|
186
|
-
source_function=f"{self.__class__.__name__}.update",
|
220
|
+
source_function=f"base_service.{self.__class__.__name__}.update",
|
187
221
|
original_error=e
|
188
222
|
)
|
189
223
|
|
@@ -201,14 +235,14 @@ class BaseService(Generic[ModelType]):
|
|
201
235
|
raise CustomException(
|
202
236
|
ErrorCode.VALIDATION_ERROR,
|
203
237
|
detail=ulid,
|
204
|
-
source_function=f"{self.__class__.__name__}.delete"
|
238
|
+
source_function=f"base_service.{self.__class__.__name__}.delete.ulid_validation"
|
205
239
|
)
|
206
240
|
|
207
241
|
if not ulid and not conditions:
|
208
242
|
raise CustomException(
|
209
243
|
ErrorCode.INVALID_INPUT,
|
210
244
|
detail="Either 'ulid' or 'conditions' must be provided.",
|
211
|
-
source_function="
|
245
|
+
source_function=f"base_service.{self.__class__.__name__}.delete.ulid_or_conditions"
|
212
246
|
)
|
213
247
|
|
214
248
|
# ulid로 조건 생성
|
@@ -226,7 +260,7 @@ class BaseService(Generic[ModelType]):
|
|
226
260
|
raise CustomException(
|
227
261
|
ErrorCode.INTERNAL_ERROR,
|
228
262
|
detail=str(e),
|
229
|
-
source_function=f"{self.__class__.__name__}.delete",
|
263
|
+
source_function=f"base_service.{self.__class__.__name__}.delete",
|
230
264
|
original_error=e
|
231
265
|
)
|
232
266
|
|
@@ -282,7 +316,7 @@ class BaseService(Generic[ModelType]):
|
|
282
316
|
except Exception as e:
|
283
317
|
raise CustomException(
|
284
318
|
ErrorCode.INTERNAL_ERROR,
|
285
|
-
source_function=f"{self.__class__.__name__}.list",
|
319
|
+
source_function=f"base_service.{self.__class__.__name__}.list",
|
286
320
|
original_error=e
|
287
321
|
)
|
288
322
|
|
@@ -304,7 +338,7 @@ class BaseService(Generic[ModelType]):
|
|
304
338
|
raise CustomException(
|
305
339
|
ErrorCode.INVALID_INPUT,
|
306
340
|
detail="Either 'ulid' or 'conditions' must be provided.",
|
307
|
-
source_function="
|
341
|
+
source_function=f"base_service.{self.__class__.__name__}.get.ulid_or_conditions"
|
308
342
|
)
|
309
343
|
|
310
344
|
# ulid로 조건 생성
|
@@ -313,7 +347,7 @@ class BaseService(Generic[ModelType]):
|
|
313
347
|
raise CustomException(
|
314
348
|
ErrorCode.VALIDATION_ERROR,
|
315
349
|
detail=ulid,
|
316
|
-
source_function=f"{self.__class__.__name__}.
|
350
|
+
source_function=f"base_service.{self.__class__.__name__}.get.ulid_validation"
|
317
351
|
)
|
318
352
|
|
319
353
|
conditions = {"ulid": ulid}
|
@@ -331,7 +365,7 @@ class BaseService(Generic[ModelType]):
|
|
331
365
|
raise CustomException(
|
332
366
|
ErrorCode.INTERNAL_ERROR,
|
333
367
|
detail=str(e),
|
334
|
-
source_function=f"{self.__class__.__name__}.get",
|
368
|
+
source_function=f"base_service.{self.__class__.__name__}.get",
|
335
369
|
original_error=e
|
336
370
|
)
|
337
371
|
|
aiteamutils/database.py
CHANGED
@@ -341,7 +341,7 @@ async def update_entity(
|
|
341
341
|
raise CustomException(
|
342
342
|
ErrorCode.NOT_FOUND,
|
343
343
|
detail=f"{model.__name__}|{conditions}.",
|
344
|
-
source_function="database.update_entity"
|
344
|
+
source_function="database.update_entity.not_entity"
|
345
345
|
)
|
346
346
|
|
347
347
|
# 기존 데이터를 딕셔너리로 변환
|
@@ -394,7 +394,7 @@ async def delete_entity(
|
|
394
394
|
raise CustomException(
|
395
395
|
ErrorCode.NOT_FOUND,
|
396
396
|
detail=f"{model.__name__}|{conditions}.",
|
397
|
-
source_function="database.delete_entity"
|
397
|
+
source_function="database.delete_entity.not_entity"
|
398
398
|
)
|
399
399
|
|
400
400
|
entity.is_deleted = True
|
aiteamutils/validators.py
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
"""유효성 검사 관련 유틸리티 함수들을 모아둔 모듈입니다."""
|
2
2
|
|
3
|
-
from typing import Type, Dict, Any, Callable, TypeVar, Optional, List, Union
|
3
|
+
from typing import Type, Dict, Any, Callable, TypeVar, Optional, List, Union, Annotated
|
4
4
|
from functools import wraps
|
5
5
|
from sqlalchemy import Table
|
6
6
|
from sqlalchemy.ext.asyncio import AsyncSession
|
7
7
|
from fastapi import Request
|
8
8
|
from inspect import signature
|
9
|
-
from pydantic import BaseModel, field_validator
|
9
|
+
from pydantic import BaseModel, field_validator, Field
|
10
10
|
from datetime import datetime, date
|
11
11
|
import re
|
12
12
|
|
@@ -234,64 +234,3 @@ class Validator:
|
|
234
234
|
source_function="Validator.validate_datetime",
|
235
235
|
original_error=str(e)
|
236
236
|
)
|
237
|
-
|
238
|
-
def _create_validator(field_names: tuple[str, ...],
|
239
|
-
validate_func: Callable,
|
240
|
-
field_type: Type,
|
241
|
-
error_code: ErrorCode,
|
242
|
-
source_prefix: str):
|
243
|
-
"""공통 validator 생성 함수
|
244
|
-
Args:
|
245
|
-
field_names: 검증할 필드명들
|
246
|
-
validate_func: 실제 검증을 수행할 함수
|
247
|
-
field_type: 필드 타입 (date 또는 datetime)
|
248
|
-
error_code: 에러 발생시 사용할 에러 코드
|
249
|
-
source_prefix: 에러 발생시 사용할 source_function 접두사
|
250
|
-
"""
|
251
|
-
def decorator(cls):
|
252
|
-
for field_name in field_names:
|
253
|
-
field = cls.model_fields.get(field_name)
|
254
|
-
if field:
|
255
|
-
field.annotation = Optional[field_type]
|
256
|
-
field.default = None
|
257
|
-
|
258
|
-
@field_validator(field_name, mode='before')
|
259
|
-
@classmethod
|
260
|
-
def validate(cls, value: Any, info: Any) -> Any:
|
261
|
-
# 빈 값 처리를 가장 먼저, 더 엄격하게
|
262
|
-
if not value or str(value).strip() == "":
|
263
|
-
return None
|
264
|
-
try:
|
265
|
-
return validate_func(value, field_name)
|
266
|
-
except CustomException as e:
|
267
|
-
raise e
|
268
|
-
except Exception as e:
|
269
|
-
raise CustomException(
|
270
|
-
error_code=error_code,
|
271
|
-
detail=f"{field_name}|{value}",
|
272
|
-
source_function=f"{source_prefix}.{field_name}",
|
273
|
-
original_error=str(e)
|
274
|
-
)
|
275
|
-
setattr(cls, f'validate_{field_name}', validate)
|
276
|
-
return cls
|
277
|
-
return decorator
|
278
|
-
|
279
|
-
def date_validator(*field_names: str):
|
280
|
-
"""날짜 필드 유효성 검사 데코레이터"""
|
281
|
-
return _create_validator(
|
282
|
-
field_names=field_names,
|
283
|
-
validate_func=Validator.validate_date,
|
284
|
-
field_type=date,
|
285
|
-
error_code=ErrorCode.VALIDATION_ERROR,
|
286
|
-
source_prefix="date_validator"
|
287
|
-
)
|
288
|
-
|
289
|
-
def datetime_validator(*field_names: str):
|
290
|
-
"""날짜+시간 필드 유효성 검사 데코레이터"""
|
291
|
-
return _create_validator(
|
292
|
-
field_names=field_names,
|
293
|
-
validate_func=Validator.validate_datetime,
|
294
|
-
field_type=datetime,
|
295
|
-
error_code=ErrorCode.VALIDATION_ERROR,
|
296
|
-
source_prefix="datetime_validator"
|
297
|
-
)
|
aiteamutils/version.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
"""버전 정보"""
|
2
|
-
__version__ = "0.2.
|
2
|
+
__version__ = "0.2.138"
|
@@ -1,16 +1,16 @@
|
|
1
1
|
aiteamutils/__init__.py,sha256=kRBpRjark0M8ZwFfmKiMFol6CbIILN3WE4f6_P6iIq0,1089
|
2
2
|
aiteamutils/base_model.py,sha256=0rs4cjnF2ea3Q2vBTj6F64BGk7ZglJsChsS7ne_R_tg,4056
|
3
|
-
aiteamutils/base_repository.py,sha256=
|
4
|
-
aiteamutils/base_service.py,sha256=
|
3
|
+
aiteamutils/base_repository.py,sha256=Oy2zE1i5qx60Xf1tnsaKLyFWapiPqt5JH8NejwNrPWg,4647
|
4
|
+
aiteamutils/base_service.py,sha256=QSUzdIcV88EdmjEF7vMyrN5CjKhS6HTbsoXSp8P9Gag,14432
|
5
5
|
aiteamutils/cache.py,sha256=07xBGlgAwOTAdY5mnMOQJ5EBxVwe8glVD7DkGEkxCtw,1373
|
6
6
|
aiteamutils/config.py,sha256=YdalpJb70-txhGJAS4aaKglEZAFVWgfzw5BXSWpkUz4,3232
|
7
|
-
aiteamutils/database.py,sha256=
|
7
|
+
aiteamutils/database.py,sha256=msvBKtxWeQVOo0v2Q9i2azuTNtnUItuNNar52gdRZTo,20418
|
8
8
|
aiteamutils/enums.py,sha256=7WLqlcJqQWtETAga2WAxNp3dJTQIAd2TW-4WzkoHHa8,2498
|
9
9
|
aiteamutils/exceptions.py,sha256=pgf3ersezObyl17wAO3I2fb8m9t2OzWDX1mSjwAWm2Y,16035
|
10
10
|
aiteamutils/files.py,sha256=tdvivl3XLNv7Al7H1gGFczmrHM8XlQpiZsEc2xQ_UTU,8829
|
11
11
|
aiteamutils/security.py,sha256=McUl3t5Z5SyUDVUHymHdDkYyF4YSeg4g9fFMML4W6Kw,11630
|
12
|
-
aiteamutils/validators.py,sha256=
|
13
|
-
aiteamutils/version.py,sha256=
|
14
|
-
aiteamutils-0.2.
|
15
|
-
aiteamutils-0.2.
|
16
|
-
aiteamutils-0.2.
|
12
|
+
aiteamutils/validators.py,sha256=_WHN6jqJQzKM5uPTg-Da8U2qqevS84XeKMkCCF4C_lY,9591
|
13
|
+
aiteamutils/version.py,sha256=xZySMcOyLEq-gP55_1pxieeIS9BbYr1Tg7GT4OJHMjI,43
|
14
|
+
aiteamutils-0.2.138.dist-info/METADATA,sha256=Hc55YOzeewQdyvH7AGj17BxER6ZiOIptMcPCY87XyXE,1719
|
15
|
+
aiteamutils-0.2.138.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
16
|
+
aiteamutils-0.2.138.dist-info/RECORD,,
|
File without changes
|