geek-cafe-saas-sdk 0.7.0__py3-none-any.whl → 0.7.1__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/models/directory.py +42 -6
- geek_cafe_saas_sdk/domains/files/models/file.py +40 -4
- geek_cafe_saas_sdk/domains/files/models/file_share.py +33 -0
- geek_cafe_saas_sdk/domains/files/models/file_version.py +24 -0
- geek_cafe_saas_sdk/domains/files/services/directory_service.py +54 -135
- geek_cafe_saas_sdk/domains/files/services/file_share_service.py +37 -120
- geek_cafe_saas_sdk/domains/files/services/file_system_service.py +40 -102
- geek_cafe_saas_sdk/domains/files/services/file_version_service.py +44 -124
- geek_cafe_saas_sdk/domains/messaging/services/contact_thread_service.py +55 -7
- geek_cafe_saas_sdk/domains/notifications/__init__.py +18 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/__init__.py +1 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/create_webhook/app.py +73 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/get/app.py +40 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/get_preferences/app.py +34 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/list/app.py +43 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/list_webhooks/app.py +40 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/mark_read/app.py +40 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/send/app.py +83 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/update_preferences/app.py +45 -0
- geek_cafe_saas_sdk/domains/notifications/models/__init__.py +16 -0
- geek_cafe_saas_sdk/domains/notifications/models/notification.py +717 -0
- geek_cafe_saas_sdk/domains/notifications/models/notification_preference.py +365 -0
- geek_cafe_saas_sdk/domains/notifications/models/webhook_subscription.py +339 -0
- geek_cafe_saas_sdk/domains/notifications/services/__init__.py +10 -0
- geek_cafe_saas_sdk/domains/notifications/services/notification_service.py +576 -0
- geek_cafe_saas_sdk/domains/payments/__init__.py +16 -0
- geek_cafe_saas_sdk/domains/payments/handlers/README.md +334 -0
- geek_cafe_saas_sdk/domains/payments/handlers/__init__.py +6 -0
- geek_cafe_saas_sdk/domains/payments/handlers/billing_accounts/create/app.py +105 -0
- geek_cafe_saas_sdk/domains/payments/handlers/billing_accounts/get/app.py +60 -0
- geek_cafe_saas_sdk/domains/payments/handlers/billing_accounts/update/app.py +97 -0
- geek_cafe_saas_sdk/domains/payments/handlers/payment_intents/create/app.py +97 -0
- geek_cafe_saas_sdk/domains/payments/handlers/payment_intents/get/app.py +60 -0
- geek_cafe_saas_sdk/domains/payments/handlers/payments/get/app.py +60 -0
- geek_cafe_saas_sdk/domains/payments/handlers/payments/list/app.py +68 -0
- geek_cafe_saas_sdk/domains/payments/handlers/payments/record/app.py +118 -0
- geek_cafe_saas_sdk/domains/payments/handlers/refunds/create/app.py +89 -0
- geek_cafe_saas_sdk/domains/payments/handlers/refunds/get/app.py +60 -0
- geek_cafe_saas_sdk/domains/payments/models/__init__.py +17 -0
- geek_cafe_saas_sdk/domains/payments/models/billing_account.py +521 -0
- geek_cafe_saas_sdk/domains/payments/models/payment.py +639 -0
- geek_cafe_saas_sdk/domains/payments/models/payment_intent_ref.py +539 -0
- geek_cafe_saas_sdk/domains/payments/models/refund.py +404 -0
- geek_cafe_saas_sdk/domains/payments/services/__init__.py +11 -0
- geek_cafe_saas_sdk/domains/payments/services/payment_service.py +405 -0
- geek_cafe_saas_sdk/domains/subscriptions/__init__.py +19 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/README.md +408 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/__init__.py +1 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/addons/create/app.py +81 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/addons/get/app.py +48 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/addons/list/app.py +54 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/addons/update/app.py +54 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/discounts/create/app.py +83 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/discounts/get/app.py +47 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/discounts/validate/app.py +62 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/plans/create/app.py +82 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/plans/get/app.py +48 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/plans/list/app.py +66 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/plans/update/app.py +54 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/usage/aggregate/app.py +72 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/usage/record/app.py +89 -0
- geek_cafe_saas_sdk/domains/subscriptions/models/__init__.py +13 -0
- geek_cafe_saas_sdk/domains/subscriptions/models/addon.py +604 -0
- geek_cafe_saas_sdk/domains/subscriptions/models/discount.py +492 -0
- geek_cafe_saas_sdk/domains/subscriptions/models/plan.py +569 -0
- geek_cafe_saas_sdk/domains/subscriptions/models/usage_record.py +300 -0
- geek_cafe_saas_sdk/domains/subscriptions/services/__init__.py +10 -0
- geek_cafe_saas_sdk/domains/subscriptions/services/subscription_manager_service.py +694 -0
- geek_cafe_saas_sdk/domains/tenancy/models/subscription.py +123 -1
- geek_cafe_saas_sdk/domains/tenancy/services/subscription_service.py +213 -0
- geek_cafe_saas_sdk/lambda_handlers/_base/base_handler.py +7 -0
- geek_cafe_saas_sdk/services/database_service.py +10 -6
- geek_cafe_saas_sdk/utilities/environment_variables.py +16 -0
- geek_cafe_saas_sdk/utilities/logging_utility.py +77 -0
- {geek_cafe_saas_sdk-0.7.0.dist-info → geek_cafe_saas_sdk-0.7.1.dist-info}/METADATA +1 -1
- {geek_cafe_saas_sdk-0.7.0.dist-info → geek_cafe_saas_sdk-0.7.1.dist-info}/RECORD +79 -20
- {geek_cafe_saas_sdk-0.7.0.dist-info → geek_cafe_saas_sdk-0.7.1.dist-info}/WHEEL +0 -0
- {geek_cafe_saas_sdk-0.7.0.dist-info → geek_cafe_saas_sdk-0.7.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -109,27 +109,8 @@ class FileShareService(DatabaseService[FileShare]):
|
|
|
109
109
|
share.access_count = 0
|
|
110
110
|
|
|
111
111
|
# Save to DynamoDB
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
item = share.to_dictionary()
|
|
116
|
-
item["pk"] = pk
|
|
117
|
-
item["sk"] = sk
|
|
118
|
-
|
|
119
|
-
# GSI1: Shares by file
|
|
120
|
-
item["gsi1_pk"] = f"FILE#{tenant_id}#{file_id}"
|
|
121
|
-
item["gsi1_sk"] = f"USER#{shared_with_user_id}"
|
|
122
|
-
|
|
123
|
-
# GSI2: Shares with user
|
|
124
|
-
item["gsi2_pk"] = f"TENANT#{tenant_id}#USER#{shared_with_user_id}"
|
|
125
|
-
item["gsi2_sk"] = f"FILE#{file_id}#{share.created_utc_ts}"
|
|
126
|
-
|
|
127
|
-
self.dynamodb.save(
|
|
128
|
-
item=item,
|
|
129
|
-
table_name=self.table_name
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
return ServiceResult.success_result(share)
|
|
112
|
+
share.prep_for_save()
|
|
113
|
+
return self._save_model(share)
|
|
133
114
|
|
|
134
115
|
except (ValidationError, AccessDeniedError) as e:
|
|
135
116
|
return ServiceResult.error_result(
|
|
@@ -163,23 +144,12 @@ class FileShareService(DatabaseService[FileShare]):
|
|
|
163
144
|
ServiceResult with FileShare model
|
|
164
145
|
"""
|
|
165
146
|
try:
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
pk = f"FILE#{tenant_id}#{file_id}"
|
|
170
|
-
sk = f"SHARE#{resource_id}"
|
|
147
|
+
# Use helper method with tenant check
|
|
148
|
+
share = self._get_model_by_id_with_tenant_check(resource_id, FileShare, tenant_id)
|
|
171
149
|
|
|
172
|
-
|
|
173
|
-
table_name=self.table_name,
|
|
174
|
-
key={"pk": pk, "sk": sk}
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
if not result or 'Item' not in result:
|
|
150
|
+
if not share:
|
|
178
151
|
raise NotFoundError(f"Share not found: {resource_id}")
|
|
179
152
|
|
|
180
|
-
share = FileShare()
|
|
181
|
-
share.map(result['Item'])
|
|
182
|
-
|
|
183
153
|
# Access control: user must be sharer or sharee
|
|
184
154
|
if share.shared_by != user_id and share.shared_with_user_id != user_id:
|
|
185
155
|
raise AccessDeniedError("You do not have access to this share")
|
|
@@ -256,25 +226,8 @@ class FileShareService(DatabaseService[FileShare]):
|
|
|
256
226
|
share.updated_utc_ts = dt.datetime.now(dt.UTC).timestamp()
|
|
257
227
|
|
|
258
228
|
# Save to DynamoDB
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
item = share.to_dictionary()
|
|
263
|
-
item["pk"] = pk
|
|
264
|
-
item["sk"] = sk
|
|
265
|
-
|
|
266
|
-
# Preserve GSI keys
|
|
267
|
-
item["gsi1_pk"] = f"FILE#{tenant_id}#{file_id}"
|
|
268
|
-
item["gsi1_sk"] = f"USER#{share.shared_with_user_id}"
|
|
269
|
-
item["gsi2_pk"] = f"TENANT#{tenant_id}#USER#{share.shared_with_user_id}"
|
|
270
|
-
item["gsi2_sk"] = f"FILE#{file_id}#{share.created_utc_ts}"
|
|
271
|
-
|
|
272
|
-
self.dynamodb.save(
|
|
273
|
-
item=item,
|
|
274
|
-
table_name=self.table_name
|
|
275
|
-
)
|
|
276
|
-
|
|
277
|
-
return ServiceResult.success_result(share)
|
|
229
|
+
share.prep_for_save()
|
|
230
|
+
return self._save_model(share)
|
|
278
231
|
|
|
279
232
|
except (ValidationError, AccessDeniedError) as e:
|
|
280
233
|
return ServiceResult.error_result(
|
|
@@ -323,23 +276,11 @@ class FileShareService(DatabaseService[FileShare]):
|
|
|
323
276
|
share.status = "revoked"
|
|
324
277
|
share.revoked_at = dt.datetime.now(dt.UTC).timestamp()
|
|
325
278
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
item = share.to_dictionary()
|
|
330
|
-
item["pk"] = pk
|
|
331
|
-
item["sk"] = sk
|
|
332
|
-
|
|
333
|
-
# Preserve GSI keys
|
|
334
|
-
item["gsi1_pk"] = f"FILE#{tenant_id}#{file_id}"
|
|
335
|
-
item["gsi1_sk"] = f"USER#{share.shared_with_user_id}"
|
|
336
|
-
item["gsi2_pk"] = f"TENANT#{tenant_id}#USER#{share.shared_with_user_id}"
|
|
337
|
-
item["gsi2_sk"] = f"FILE#{file_id}#{share.created_utc_ts}"
|
|
279
|
+
share.prep_for_save()
|
|
280
|
+
save_result = self._save_model(share)
|
|
338
281
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
table_name=self.table_name
|
|
342
|
-
)
|
|
282
|
+
if not save_result.success:
|
|
283
|
+
return save_result
|
|
343
284
|
|
|
344
285
|
return ServiceResult.success_result(True)
|
|
345
286
|
|
|
@@ -383,21 +324,19 @@ class FileShareService(DatabaseService[FileShare]):
|
|
|
383
324
|
error_code=ErrorCode.ACCESS_DENIED
|
|
384
325
|
)
|
|
385
326
|
|
|
386
|
-
#
|
|
387
|
-
|
|
327
|
+
# Use GSI1 to query shares by file
|
|
328
|
+
temp_share = FileShare()
|
|
329
|
+
temp_share.file_id = file_id
|
|
388
330
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
table_name=self.table_name,
|
|
392
|
-
index_name="gsi1",
|
|
393
|
-
limit=limit
|
|
394
|
-
)
|
|
331
|
+
# Query using helper method
|
|
332
|
+
query_result = self._query_by_index(temp_share, "gsi1", limit=limit, ascending=False)
|
|
395
333
|
|
|
334
|
+
if not query_result.success:
|
|
335
|
+
return query_result
|
|
336
|
+
|
|
337
|
+
# Filter results
|
|
396
338
|
shares = []
|
|
397
|
-
for
|
|
398
|
-
share = FileShare()
|
|
399
|
-
share.map(item)
|
|
400
|
-
|
|
339
|
+
for share in query_result.data:
|
|
401
340
|
# Include active and expired shares, exclude revoked
|
|
402
341
|
if share.status != "revoked":
|
|
403
342
|
shares.append(share)
|
|
@@ -429,21 +368,19 @@ class FileShareService(DatabaseService[FileShare]):
|
|
|
429
368
|
ServiceResult with list of FileShare models
|
|
430
369
|
"""
|
|
431
370
|
try:
|
|
432
|
-
#
|
|
433
|
-
|
|
371
|
+
# Use GSI2 to query shares by shared_with_user
|
|
372
|
+
temp_share = FileShare()
|
|
373
|
+
temp_share.shared_with_user_id = user_id
|
|
434
374
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
)
|
|
375
|
+
# Query using helper method
|
|
376
|
+
query_result = self._query_by_index(temp_share, "gsi2", limit=limit, ascending=False)
|
|
377
|
+
|
|
378
|
+
if not query_result.success:
|
|
379
|
+
return query_result
|
|
441
380
|
|
|
381
|
+
# Filter results
|
|
442
382
|
shares = []
|
|
443
|
-
for
|
|
444
|
-
share = FileShare()
|
|
445
|
-
share.map(item)
|
|
446
|
-
|
|
383
|
+
for share in query_result.data:
|
|
447
384
|
# Only include active, non-expired shares
|
|
448
385
|
if share.is_active:
|
|
449
386
|
shares.append(share)
|
|
@@ -543,20 +480,12 @@ class FileShareService(DatabaseService[FileShare]):
|
|
|
543
480
|
def _get_file(self, tenant_id: str, file_id: str, user_id: str) -> ServiceResult[File]:
|
|
544
481
|
"""Get file with access control."""
|
|
545
482
|
try:
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
result = self.dynamodb.get(
|
|
550
|
-
table_name=self.table_name,
|
|
551
|
-
key={"pk": pk, "sk": sk}
|
|
552
|
-
)
|
|
483
|
+
# Use helper method with tenant check
|
|
484
|
+
file = self._get_model_by_id_with_tenant_check(file_id, File, tenant_id)
|
|
553
485
|
|
|
554
|
-
if not
|
|
486
|
+
if not file:
|
|
555
487
|
raise NotFoundError(f"File not found: {file_id}")
|
|
556
488
|
|
|
557
|
-
file = File()
|
|
558
|
-
file.map(result['Item'])
|
|
559
|
-
|
|
560
489
|
if file.owner_id != user_id:
|
|
561
490
|
raise AccessDeniedError("You do not have access to this file")
|
|
562
491
|
|
|
@@ -572,7 +501,7 @@ class FileShareService(DatabaseService[FileShare]):
|
|
|
572
501
|
"""Get file without access control (for internal use)."""
|
|
573
502
|
try:
|
|
574
503
|
pk = f"FILE#{tenant_id}#{file_id}"
|
|
575
|
-
sk = "
|
|
504
|
+
sk = "metadata"
|
|
576
505
|
|
|
577
506
|
result = self.dynamodb.get(
|
|
578
507
|
table_name=self.table_name,
|
|
@@ -645,19 +574,7 @@ class FileShareService(DatabaseService[FileShare]):
|
|
|
645
574
|
share.access_count += 1
|
|
646
575
|
share.last_accessed_at = dt.datetime.now(dt.UTC).timestamp()
|
|
647
576
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
item["sk"] = sk
|
|
651
|
-
|
|
652
|
-
# Preserve GSI keys
|
|
653
|
-
item["gsi1_pk"] = result['Item'].get('gsi1_pk')
|
|
654
|
-
item["gsi1_sk"] = result['Item'].get('gsi1_sk')
|
|
655
|
-
item["gsi2_pk"] = result['Item'].get('gsi2_pk')
|
|
656
|
-
item["gsi2_sk"] = result['Item'].get('gsi2_sk')
|
|
657
|
-
|
|
658
|
-
self.dynamodb.save(
|
|
659
|
-
item=item,
|
|
660
|
-
table_name=self.table_name
|
|
661
|
-
)
|
|
577
|
+
share.prep_for_save()
|
|
578
|
+
self._save_model(share)
|
|
662
579
|
except Exception:
|
|
663
580
|
pass # Best effort
|
|
@@ -174,28 +174,11 @@ class FileSystemService(DatabaseService[File]):
|
|
|
174
174
|
)
|
|
175
175
|
|
|
176
176
|
# Save metadata to DynamoDB
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
item = file.to_dictionary()
|
|
181
|
-
item["pk"] = pk
|
|
182
|
-
item["sk"] = sk
|
|
183
|
-
|
|
184
|
-
# GSI1: Files by directory
|
|
185
|
-
item["gsi1_pk"] = f"TENANT#{tenant_id}"
|
|
186
|
-
if directory_id:
|
|
187
|
-
item["gsi1_sk"] = f"DIRECTORY#{directory_id}#{file_name}"
|
|
188
|
-
else:
|
|
189
|
-
item["gsi1_sk"] = f"DIRECTORY#ROOT#{file_name}"
|
|
190
|
-
|
|
191
|
-
# GSI2: Files by owner
|
|
192
|
-
item["gsi2_pk"] = f"TENANT#{tenant_id}#USER#{user_id}"
|
|
193
|
-
item["gsi2_sk"] = f"FILE#{file.created_utc_ts}"
|
|
177
|
+
file.prep_for_save()
|
|
178
|
+
save_result = self._save_model(file)
|
|
194
179
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
item=item
|
|
198
|
-
)
|
|
180
|
+
if not save_result.success:
|
|
181
|
+
return save_result
|
|
199
182
|
|
|
200
183
|
return ServiceResult.success_result(file)
|
|
201
184
|
|
|
@@ -229,23 +212,13 @@ class FileSystemService(DatabaseService[File]):
|
|
|
229
212
|
ServiceResult with File model
|
|
230
213
|
"""
|
|
231
214
|
try:
|
|
232
|
-
|
|
233
|
-
|
|
215
|
+
# Use helper method with tenant check
|
|
216
|
+
file = self._get_model_by_id_with_tenant_check(resource_id, File, tenant_id)
|
|
234
217
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
key={"pk": pk, "sk": sk}
|
|
238
|
-
)
|
|
239
|
-
|
|
240
|
-
# Check if file exists first (before checking ownership)
|
|
241
|
-
# DynamoDB.get returns {'Item': {...}} or {'ResponseMetadata': {...}}
|
|
242
|
-
if not result or 'Item' not in result:
|
|
218
|
+
# Check if file exists
|
|
219
|
+
if not file:
|
|
243
220
|
raise NotFoundError(f"File not found: {resource_id}")
|
|
244
221
|
|
|
245
|
-
# Convert to File model
|
|
246
|
-
file = File()
|
|
247
|
-
file.map(result['Item'])
|
|
248
|
-
|
|
249
222
|
# Access control: Check if user is owner or has share access
|
|
250
223
|
if file.owner_id != user_id:
|
|
251
224
|
# TODO: Check FileShare for access
|
|
@@ -316,27 +289,8 @@ class FileSystemService(DatabaseService[File]):
|
|
|
316
289
|
file.updated_utc_ts = dt.datetime.now(dt.UTC).timestamp()
|
|
317
290
|
|
|
318
291
|
# Save to DynamoDB
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
item = file.to_dictionary()
|
|
323
|
-
item["pk"] = pk
|
|
324
|
-
item["sk"] = sk
|
|
325
|
-
|
|
326
|
-
# Update GSI keys if directory changed
|
|
327
|
-
if "directory_id" in updates:
|
|
328
|
-
item["gsi1_pk"] = f"TENANT#{tenant_id}"
|
|
329
|
-
if updates["directory_id"]:
|
|
330
|
-
item["gsi1_sk"] = f"DIRECTORY#{updates['directory_id']}#{file.file_name}"
|
|
331
|
-
else:
|
|
332
|
-
item["gsi1_sk"] = f"DIRECTORY#ROOT#{file.file_name}"
|
|
333
|
-
|
|
334
|
-
self.dynamodb.save(
|
|
335
|
-
table_name=self.table_name,
|
|
336
|
-
item=item
|
|
337
|
-
)
|
|
338
|
-
|
|
339
|
-
return ServiceResult.success_result(file)
|
|
292
|
+
file.prep_for_save()
|
|
293
|
+
return self._save_model(file)
|
|
340
294
|
|
|
341
295
|
except AccessDeniedError as e:
|
|
342
296
|
return ServiceResult.error_result(
|
|
@@ -397,7 +351,7 @@ class FileSystemService(DatabaseService[File]):
|
|
|
397
351
|
|
|
398
352
|
# Delete from DynamoDB
|
|
399
353
|
pk = f"FILE#{tenant_id}#{resource_id}"
|
|
400
|
-
sk = "
|
|
354
|
+
sk = "metadata"
|
|
401
355
|
|
|
402
356
|
self.dynamodb.delete(
|
|
403
357
|
primary_key={"pk": pk, "sk": sk},
|
|
@@ -409,17 +363,11 @@ class FileSystemService(DatabaseService[File]):
|
|
|
409
363
|
file.status = "deleted"
|
|
410
364
|
file.deleted_utc_ts = dt.datetime.now(dt.UTC).timestamp()
|
|
411
365
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
item = file.to_dictionary()
|
|
416
|
-
item["pk"] = pk
|
|
417
|
-
item["sk"] = sk
|
|
366
|
+
file.prep_for_save()
|
|
367
|
+
save_result = self._save_model(file)
|
|
418
368
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
item=item
|
|
422
|
-
)
|
|
369
|
+
if not save_result.success:
|
|
370
|
+
return save_result
|
|
423
371
|
|
|
424
372
|
return ServiceResult.success_result(True)
|
|
425
373
|
|
|
@@ -507,32 +455,23 @@ class FileSystemService(DatabaseService[File]):
|
|
|
507
455
|
ServiceResult with list of File models
|
|
508
456
|
"""
|
|
509
457
|
try:
|
|
510
|
-
|
|
458
|
+
# Use GSI1 to query files by directory
|
|
459
|
+
temp_file = File()
|
|
460
|
+
temp_file.tenant_id = tenant_id
|
|
461
|
+
temp_file.directory_id = directory_id # None for root files
|
|
511
462
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
# Query GSI1
|
|
518
|
-
results = self.dynamodb.query(
|
|
519
|
-
key=Key('gsi1_pk').eq(gsi1_pk) & Key('gsi1_sk').begins_with(gsi1_sk_prefix),
|
|
520
|
-
table_name=self.table_name,
|
|
521
|
-
index_name="gsi1",
|
|
522
|
-
limit=limit
|
|
523
|
-
)
|
|
463
|
+
# Query using helper method
|
|
464
|
+
query_result = self._query_by_index(temp_file, "gsi1", limit=limit, ascending=True)
|
|
465
|
+
|
|
466
|
+
if not query_result.success:
|
|
467
|
+
return query_result
|
|
524
468
|
|
|
469
|
+
# Filter results
|
|
525
470
|
files = []
|
|
526
|
-
for
|
|
527
|
-
|
|
528
|
-
file.
|
|
529
|
-
|
|
530
|
-
# Filter out deleted files
|
|
531
|
-
if file.status != "deleted":
|
|
532
|
-
# Basic access control: show only owned files or shared files
|
|
533
|
-
# TODO: Check FileShare for shared access
|
|
534
|
-
if file.owner_id == user_id:
|
|
535
|
-
files.append(file)
|
|
471
|
+
for file in query_result.data:
|
|
472
|
+
# Filter out deleted files and apply access control
|
|
473
|
+
if file.status != "deleted" and file.owner_id == user_id:
|
|
474
|
+
files.append(file)
|
|
536
475
|
|
|
537
476
|
return ServiceResult.success_result(files)
|
|
538
477
|
|
|
@@ -567,21 +506,20 @@ class FileSystemService(DatabaseService[File]):
|
|
|
567
506
|
if owner_id != user_id:
|
|
568
507
|
raise AccessDeniedError("You can only list your own files")
|
|
569
508
|
|
|
570
|
-
|
|
509
|
+
# Use GSI2 to query files by owner
|
|
510
|
+
temp_file = File()
|
|
511
|
+
temp_file.tenant_id = tenant_id
|
|
512
|
+
temp_file.owner_id = owner_id
|
|
571
513
|
|
|
572
|
-
# Query
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
limit=limit
|
|
578
|
-
)
|
|
514
|
+
# Query using helper method
|
|
515
|
+
query_result = self._query_by_index(temp_file, "gsi2", limit=limit, ascending=False)
|
|
516
|
+
|
|
517
|
+
if not query_result.success:
|
|
518
|
+
return query_result
|
|
579
519
|
|
|
520
|
+
# Filter results
|
|
580
521
|
files = []
|
|
581
|
-
for
|
|
582
|
-
file = File()
|
|
583
|
-
file.map(item)
|
|
584
|
-
|
|
522
|
+
for file in query_result.data:
|
|
585
523
|
# Filter out deleted files
|
|
586
524
|
if file.status != "deleted":
|
|
587
525
|
files.append(file)
|