geek-cafe-saas-sdk 0.7.2__py3-none-any.whl → 0.7.3__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.
Potentially problematic release.
This version of geek-cafe-saas-sdk might be problematic. Click here for more details.
- geek_cafe_saas_sdk/__init__.py +1 -1
- geek_cafe_saas_sdk/domains/files/services/file_version_service.py +18 -43
- geek_cafe_saas_sdk/domains/messaging/models/contact_thread.py +3 -3
- geek_cafe_saas_sdk/domains/messaging/services/contact_thread_service.py +8 -1
- geek_cafe_saas_sdk/services/database_service.py +26 -1
- {geek_cafe_saas_sdk-0.7.2.dist-info → geek_cafe_saas_sdk-0.7.3.dist-info}/METADATA +1 -1
- {geek_cafe_saas_sdk-0.7.2.dist-info → geek_cafe_saas_sdk-0.7.3.dist-info}/RECORD +9 -9
- {geek_cafe_saas_sdk-0.7.2.dist-info → geek_cafe_saas_sdk-0.7.3.dist-info}/WHEEL +0 -0
- {geek_cafe_saas_sdk-0.7.2.dist-info → geek_cafe_saas_sdk-0.7.3.dist-info}/licenses/LICENSE +0 -0
geek_cafe_saas_sdk/__init__.py
CHANGED
|
@@ -515,21 +515,12 @@ class FileVersionService(DatabaseService[FileVersion]):
|
|
|
515
515
|
def _get_file(self, tenant_id: str, file_id: str, user_id: str) -> ServiceResult[File]:
|
|
516
516
|
"""Get file with access control."""
|
|
517
517
|
try:
|
|
518
|
-
|
|
519
|
-
file
|
|
520
|
-
file.tenant_id = tenant_id
|
|
518
|
+
# Use DatabaseService helper to get file
|
|
519
|
+
file = self._get_model_by_id_with_tenant_check(file_id, File, tenant_id)
|
|
521
520
|
|
|
522
|
-
|
|
523
|
-
table_name=self.table_name,
|
|
524
|
-
model=file
|
|
525
|
-
)
|
|
526
|
-
|
|
527
|
-
if not result or 'Item' not in result:
|
|
521
|
+
if not file:
|
|
528
522
|
raise NotFoundError(f"File not found: {file_id}")
|
|
529
523
|
|
|
530
|
-
file = File()
|
|
531
|
-
file.map(result['Item'])
|
|
532
|
-
|
|
533
524
|
if file.owner_id != user_id:
|
|
534
525
|
raise AccessDeniedError("You do not have access to this file")
|
|
535
526
|
|
|
@@ -544,24 +535,14 @@ class FileVersionService(DatabaseService[FileVersion]):
|
|
|
544
535
|
def _get_latest_version_number(self, tenant_id: str, file_id: str) -> int:
|
|
545
536
|
"""Get the latest version number for a file."""
|
|
546
537
|
try:
|
|
547
|
-
file
|
|
548
|
-
|
|
549
|
-
|
|
538
|
+
# Query versions for this file using DatabaseService helper
|
|
539
|
+
temp_version = FileVersion()
|
|
540
|
+
temp_version.file_id = file_id
|
|
550
541
|
|
|
551
|
-
|
|
552
|
-
results = self.dynamodb.query(
|
|
553
|
-
key=key,
|
|
554
|
-
table_name=self.table_name,
|
|
555
|
-
index_name="gsi1",
|
|
556
|
-
limit=1,
|
|
557
|
-
ascending=False # Get highest version number
|
|
558
|
-
)
|
|
542
|
+
result = self._query_by_index(temp_version, "gsi1", limit=1, ascending=False)
|
|
559
543
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
version = FileVersion()
|
|
563
|
-
version.map(items[0])
|
|
564
|
-
return version.version_number
|
|
544
|
+
if result.success and result.data:
|
|
545
|
+
return result.data[0].version_number
|
|
565
546
|
|
|
566
547
|
return 0 # No versions yet
|
|
567
548
|
|
|
@@ -576,19 +557,10 @@ class FileVersionService(DatabaseService[FileVersion]):
|
|
|
576
557
|
) -> None:
|
|
577
558
|
"""Mark a version as not current."""
|
|
578
559
|
try:
|
|
579
|
-
|
|
580
|
-
version
|
|
581
|
-
version.tenant_id = tenant_id
|
|
582
|
-
version.file_id = file_id
|
|
583
|
-
|
|
584
|
-
result = self.dynamodb.get(
|
|
585
|
-
table_name=self.table_name,
|
|
586
|
-
model=version
|
|
587
|
-
)
|
|
560
|
+
# Use DatabaseService helper to get version
|
|
561
|
+
version = self._get_model_by_id_with_tenant_check(version_id, FileVersion, tenant_id)
|
|
588
562
|
|
|
589
|
-
if
|
|
590
|
-
version = FileVersion()
|
|
591
|
-
version.map(result['Item'])
|
|
563
|
+
if version:
|
|
592
564
|
version.is_current = False
|
|
593
565
|
|
|
594
566
|
version.prep_for_save()
|
|
@@ -605,12 +577,15 @@ class FileVersionService(DatabaseService[FileVersion]):
|
|
|
605
577
|
) -> None:
|
|
606
578
|
"""Update file record with current version info."""
|
|
607
579
|
try:
|
|
608
|
-
|
|
609
|
-
|
|
580
|
+
file = File()
|
|
581
|
+
file.id = file_id
|
|
582
|
+
file.tenant_id = tenant_id
|
|
583
|
+
|
|
584
|
+
key = file.get_key("gsi1")
|
|
610
585
|
|
|
611
586
|
result = self.dynamodb.get(
|
|
612
587
|
table_name=self.table_name,
|
|
613
|
-
key=
|
|
588
|
+
key=key
|
|
614
589
|
)
|
|
615
590
|
|
|
616
591
|
if result and 'Item' in result:
|
|
@@ -33,13 +33,13 @@ class ContactThread(BaseModel):
|
|
|
33
33
|
super().__init__()
|
|
34
34
|
self._subject: str | None = None
|
|
35
35
|
self._status: str = "open" # open, in_progress, resolved, closed
|
|
36
|
-
self._priority: str = "medium" # low, medium, high, urgent
|
|
36
|
+
self._priority: Optional[str] = "medium" # low, medium, high, urgent
|
|
37
37
|
|
|
38
38
|
# Sender information (guest or authenticated user)
|
|
39
39
|
self._sender: Dict[str, Any] = {} # {id, name, email, session_id}
|
|
40
40
|
|
|
41
41
|
# Assignment and routing
|
|
42
|
-
self._assigned_to: str
|
|
42
|
+
self._assigned_to: Optional[str] = None # Staff user ID
|
|
43
43
|
self._inbox_id: str = "support" # support, sales, billing, etc.
|
|
44
44
|
|
|
45
45
|
# Messages embedded in thread (suitable for low volume)
|
|
@@ -178,7 +178,7 @@ class ContactThread(BaseModel):
|
|
|
178
178
|
|
|
179
179
|
@priority.setter
|
|
180
180
|
def priority(self, value: str | None):
|
|
181
|
-
valid_priorities = ["low", "medium", "high", "urgent"]
|
|
181
|
+
valid_priorities = ["low", "medium", "high", "urgent", None]
|
|
182
182
|
if value in valid_priorities:
|
|
183
183
|
self._priority = value
|
|
184
184
|
else:
|
|
@@ -245,7 +245,7 @@ class ContactThreadService(DatabaseService[ContactThread]):
|
|
|
245
245
|
tenant_id=tenant_id, status=status)
|
|
246
246
|
|
|
247
247
|
def list_by_assigned_user(self, assigned_to: str, tenant_id: str,
|
|
248
|
-
status: str = None, limit: int = 50) -> ServiceResult[List[ContactThread]]:
|
|
248
|
+
*, status: str = None, priority: str = None, limit: int = 50) -> ServiceResult[List[ContactThread]]:
|
|
249
249
|
"""
|
|
250
250
|
List contact threads assigned to a specific user using GSI3.
|
|
251
251
|
|
|
@@ -263,6 +263,13 @@ class ContactThreadService(DatabaseService[ContactThread]):
|
|
|
263
263
|
temp_thread.assigned_to = assigned_to
|
|
264
264
|
if status:
|
|
265
265
|
temp_thread.status = status
|
|
266
|
+
else:
|
|
267
|
+
temp_thread.status = None
|
|
268
|
+
|
|
269
|
+
if priority:
|
|
270
|
+
temp_thread.priority = priority
|
|
271
|
+
else:
|
|
272
|
+
temp_thread.priority = None
|
|
266
273
|
|
|
267
274
|
result = self._query_by_index(
|
|
268
275
|
temp_thread,
|
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
4
|
from typing import Generic, TypeVar, Dict, Any, List, Optional
|
|
5
|
-
from boto3_assist.dynamodb.dynamodb import DynamoDB
|
|
5
|
+
from boto3_assist.dynamodb.dynamodb import DynamoDB, DynamoDBIndex
|
|
6
6
|
from ..core.service_result import ServiceResult
|
|
7
7
|
from ..core.service_errors import ValidationError, AccessDeniedError, NotFoundError
|
|
8
8
|
from ..core.error_codes import ErrorCode
|
|
9
9
|
import os
|
|
10
|
+
from aws_lambda_powertools import Logger
|
|
10
11
|
|
|
11
12
|
T = TypeVar("T")
|
|
12
13
|
|
|
14
|
+
logger = Logger()
|
|
15
|
+
|
|
13
16
|
|
|
14
17
|
class DatabaseService(ABC, Generic[T]):
|
|
15
18
|
"""Base service class for database operations."""
|
|
@@ -23,6 +26,8 @@ class DatabaseService(ABC, Generic[T]):
|
|
|
23
26
|
if not self.table_name:
|
|
24
27
|
raise ValueError("Table name is required")
|
|
25
28
|
|
|
29
|
+
self.LOG_DYNAMO_DB_QUERY = os.getenv("LOG_DYNAMO_DB_QUERY", False)
|
|
30
|
+
|
|
26
31
|
@abstractmethod
|
|
27
32
|
def create(self, tenant_id: str, user_id: str, **kwargs) -> ServiceResult[T]:
|
|
28
33
|
"""Create a new resource."""
|
|
@@ -226,6 +231,26 @@ class DatabaseService(ABC, Generic[T]):
|
|
|
226
231
|
# Get the key for the specified index from the provided model
|
|
227
232
|
key = model.get_key(index_name).key()
|
|
228
233
|
|
|
234
|
+
# key.to_string()
|
|
235
|
+
# extract_key_values(key_expression) is coming soon from boto3_assist
|
|
236
|
+
if self.LOG_DYNAMO_DB_QUERY:
|
|
237
|
+
if hasattr(DynamoDBIndex, 'extract_key_values'):
|
|
238
|
+
values = DynamoDBIndex.extract_key_values(key_expression)
|
|
239
|
+
logger.info(f"Querying index {index_name} with key {values}")
|
|
240
|
+
else:
|
|
241
|
+
try:
|
|
242
|
+
value = {
|
|
243
|
+
'pk': key._values[0]._values[1],
|
|
244
|
+
'sk': key._values[1]._values[1],
|
|
245
|
+
'operator': key._values[1].expression_operator,
|
|
246
|
+
'format': key._values[1].expression_format,
|
|
247
|
+
'index_name': index_name
|
|
248
|
+
}
|
|
249
|
+
logger.info(f"Querying index {index_name} with key {value}")
|
|
250
|
+
except Exception as e:
|
|
251
|
+
logger.error(f"Failed to extract key values: {e}")
|
|
252
|
+
|
|
253
|
+
|
|
229
254
|
# Execute the query
|
|
230
255
|
response = self.dynamodb.query(
|
|
231
256
|
table_name=self.table_name,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: geek_cafe_saas_sdk
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.3
|
|
4
4
|
Summary: Base Reusable Services for SaaS
|
|
5
5
|
Project-URL: Homepage, https://github.com/geekcafe/geek-cafe-services
|
|
6
6
|
Project-URL: Documentation, https://github.com/geekcafe/geek-cafe-services/blob/main/README.md
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
geek_cafe_saas_sdk/__init__.py,sha256=
|
|
1
|
+
geek_cafe_saas_sdk/__init__.py,sha256=4M6Pbs_H8QB-sOy-gFdAxI7j6eUOW25SyD0OuAnPN98,187
|
|
2
2
|
geek_cafe_saas_sdk/core/__init__.py,sha256=3o3-n1ojO_a_X2bEfzhnxmcjAzuzAU73VTcuRva_f5U,279
|
|
3
3
|
geek_cafe_saas_sdk/core/audit_mixin.py,sha256=hQz0XYUr_Trqpv4JXxywVNxqJJehQwLu79Y1NBpRwzo,1150
|
|
4
4
|
geek_cafe_saas_sdk/core/error_codes.py,sha256=vf82TDaJ0qIQLzgjTu96nK9cAaSWK7pYfnOW8HCvSxE,5010
|
|
@@ -88,7 +88,7 @@ geek_cafe_saas_sdk/domains/files/services/directory_service.py,sha256=4WKyEB5ho_
|
|
|
88
88
|
geek_cafe_saas_sdk/domains/files/services/file_lineage_service.py,sha256=TdYPTu0fun71RtGaFzrnMI0ONSb1Je7NE8qSkBZz0eM,17545
|
|
89
89
|
geek_cafe_saas_sdk/domains/files/services/file_share_service.py,sha256=mlw2k5t8VeVJ-6RAqOBgbSuJ3F1vVC4FZpYNnRoALEU,20376
|
|
90
90
|
geek_cafe_saas_sdk/domains/files/services/file_system_service.py,sha256=uoO3RT_FZuGZxA4S3YmpXUIKymj6M8qcnL2DIyoCD9o,19079
|
|
91
|
-
geek_cafe_saas_sdk/domains/files/services/file_version_service.py,sha256=
|
|
91
|
+
geek_cafe_saas_sdk/domains/files/services/file_version_service.py,sha256=A5x8Dlo9p07Tnqwumj4JwHoonFg4Jzx4zY2sC7dDtUs,22886
|
|
92
92
|
geek_cafe_saas_sdk/domains/files/services/s3_file_service.py,sha256=T0lyUK97w-L-PrqzTRhdqxKmiW_w15k2y3BslE6WvQw,16920
|
|
93
93
|
geek_cafe_saas_sdk/domains/messaging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
94
94
|
geek_cafe_saas_sdk/domains/messaging/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -111,11 +111,11 @@ geek_cafe_saas_sdk/domains/messaging/models/__init__.py,sha256=VtJUxBdtCOxUJzXbD
|
|
|
111
111
|
geek_cafe_saas_sdk/domains/messaging/models/chat_channel.py,sha256=xrzP3gItEOIlLDaJHIRysFcpMOnAdK84sE-4pakLEkk,10565
|
|
112
112
|
geek_cafe_saas_sdk/domains/messaging/models/chat_channel_member.py,sha256=KyOfP54Ho2oU7upZa5Z-bMDwJfGq8oX5_apFCjxg4fY,6058
|
|
113
113
|
geek_cafe_saas_sdk/domains/messaging/models/chat_message.py,sha256=sFXp4hTQPlI9pTWp_K4H-XUdz7IO3FozX0PphQm0kYs,13273
|
|
114
|
-
geek_cafe_saas_sdk/domains/messaging/models/contact_thread.py,sha256=
|
|
114
|
+
geek_cafe_saas_sdk/domains/messaging/models/contact_thread.py,sha256=v4VzsY7TvuCU2YXouNHd9pMFyPl732jpQ7Hmzr6qPtw,12855
|
|
115
115
|
geek_cafe_saas_sdk/domains/messaging/services/__init__.py,sha256=qTcn7zYppwdcUSWLCCWNNorJhauCizmhlup7pqiAGXs,287
|
|
116
116
|
geek_cafe_saas_sdk/domains/messaging/services/chat_channel_service.py,sha256=ismv9N2ZKwRVErvsSXS9_IBl8kPX-AbRkAvrq4zNywM,26604
|
|
117
117
|
geek_cafe_saas_sdk/domains/messaging/services/chat_message_service.py,sha256=OVG3E-9Xz86s6OeYNY7dShil-rv9N6R6EJO54xNM948,19100
|
|
118
|
-
geek_cafe_saas_sdk/domains/messaging/services/contact_thread_service.py,sha256=
|
|
118
|
+
geek_cafe_saas_sdk/domains/messaging/services/contact_thread_service.py,sha256=k4-qecEgTB3KTFeWAeEoih8vRQR04gkRf63DefBuruE,20918
|
|
119
119
|
geek_cafe_saas_sdk/domains/notifications/__init__.py,sha256=3T6Y-gVQ9BD-RxgUVnfmk2Ss9NndwLGy2C4rNgDKV3E,424
|
|
120
120
|
geek_cafe_saas_sdk/domains/notifications/handlers/__init__.py,sha256=TV8USs_qzUi7V8IzgthzwJ69hUC-6WCREg3YN6Glhxs,32
|
|
121
121
|
geek_cafe_saas_sdk/domains/notifications/handlers/create_webhook/app.py,sha256=hQZEuj6Zj8005Q1Ix7t8cmtBN25ILJUens13mEMvS74,2087
|
|
@@ -241,7 +241,7 @@ geek_cafe_saas_sdk/middleware/validation.py,sha256=0KnHaBDHo72lAkAUIzEqGl-JCgyIm
|
|
|
241
241
|
geek_cafe_saas_sdk/models/__init__.py,sha256=M9p2n9YvwVMw9DMgMJaS-DcmmTJ7EfCmuuIFDF4wnUc,655
|
|
242
242
|
geek_cafe_saas_sdk/models/base_model.py,sha256=H-t7ZQEUwmkvV9xPyd0jVbbieAUwu9n5MCtjvqFwtYw,7353
|
|
243
243
|
geek_cafe_saas_sdk/services/__init__.py,sha256=guO3SxaTEy7NAWNG0orrsjRaUT25XvP0pY7W3lRwePY,614
|
|
244
|
-
geek_cafe_saas_sdk/services/database_service.py,sha256=
|
|
244
|
+
geek_cafe_saas_sdk/services/database_service.py,sha256=LMYhlNox8Ab-2eIbmBm6AW0EQ1fy9gWi6HP_pOFeCfs,18159
|
|
245
245
|
geek_cafe_saas_sdk/utilities/__init__.py,sha256=g8voZ8KYeFJQ6HKYhrp5q6mMHgP-AV0ezmouXRiGbDU,2064
|
|
246
246
|
geek_cafe_saas_sdk/utilities/cognito_utility.py,sha256=S36jAwtJAgeBMN6bL7WKjfLU97f78lRptzpISAQb1kQ,20208
|
|
247
247
|
geek_cafe_saas_sdk/utilities/custom_exceptions.py,sha256=Yc5Kg2kYXeYZEzuX1u48WczT9jtBZYUiWyTv7NsNDPs,5173
|
|
@@ -259,7 +259,7 @@ geek_cafe_saas_sdk/utilities/logging_utility.py,sha256=3VwPGleoRw7c4Bn43_Kujl1CQ
|
|
|
259
259
|
geek_cafe_saas_sdk/utilities/message_query_helper.py,sha256=8iMuacRPfom_T06-VMhaSp-90D8604q7waM-GVcpPNQ,11500
|
|
260
260
|
geek_cafe_saas_sdk/utilities/response.py,sha256=0sylVbm_ieIsNGsm9mWXSIdLSyOeoACwFZQQUxyim3s,5964
|
|
261
261
|
geek_cafe_saas_sdk/utilities/string_functions.py,sha256=_1F4dGyzuD3fAcV1w7A8bv1e-3p0DbNAv_i6-fsekg8,5973
|
|
262
|
-
geek_cafe_saas_sdk-0.7.
|
|
263
|
-
geek_cafe_saas_sdk-0.7.
|
|
264
|
-
geek_cafe_saas_sdk-0.7.
|
|
265
|
-
geek_cafe_saas_sdk-0.7.
|
|
262
|
+
geek_cafe_saas_sdk-0.7.3.dist-info/METADATA,sha256=Zo81XcADCmBMrVZh8AhEAdeFoiul4WJJ2ypnfiePmkM,16068
|
|
263
|
+
geek_cafe_saas_sdk-0.7.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
264
|
+
geek_cafe_saas_sdk-0.7.3.dist-info/licenses/LICENSE,sha256=EHsHc4GFN0U63LgqDSY2aQRKRupbq6QgixCwopdIU2E,2097
|
|
265
|
+
geek_cafe_saas_sdk-0.7.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|