geek-cafe-saas-sdk 0.6.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 +2 -2
- geek_cafe_saas_sdk/domains/files/handlers/README.md +446 -0
- geek_cafe_saas_sdk/domains/files/handlers/__init__.py +6 -0
- geek_cafe_saas_sdk/domains/files/handlers/files/create/app.py +121 -0
- geek_cafe_saas_sdk/domains/files/handlers/files/download/app.py +80 -0
- geek_cafe_saas_sdk/domains/files/handlers/files/get/app.py +62 -0
- geek_cafe_saas_sdk/domains/files/handlers/files/list/app.py +72 -0
- geek_cafe_saas_sdk/domains/files/handlers/lineage/create_derived/app.py +99 -0
- geek_cafe_saas_sdk/domains/files/handlers/lineage/create_main/app.py +104 -0
- geek_cafe_saas_sdk/domains/files/handlers/lineage/download_bundle/app.py +99 -0
- geek_cafe_saas_sdk/domains/files/handlers/lineage/get_lineage/app.py +68 -0
- geek_cafe_saas_sdk/domains/files/handlers/lineage/prepare_bundle/app.py +76 -0
- geek_cafe_saas_sdk/domains/files/models/__init__.py +17 -0
- geek_cafe_saas_sdk/domains/files/models/directory.py +42 -6
- geek_cafe_saas_sdk/domains/files/models/file.py +158 -16
- 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/__init__.py +21 -0
- geek_cafe_saas_sdk/domains/files/services/directory_service.py +54 -135
- geek_cafe_saas_sdk/domains/files/services/file_lineage_service.py +487 -0
- geek_cafe_saas_sdk/domains/files/services/file_share_service.py +37 -120
- geek_cafe_saas_sdk/domains/files/services/file_system_service.py +67 -103
- 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/cognito_utility.py +16 -26
- 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.6.0.dist-info → geek_cafe_saas_sdk-0.7.1.dist-info}/METADATA +11 -11
- {geek_cafe_saas_sdk-0.6.0.dist-info → geek_cafe_saas_sdk-0.7.1.dist-info}/RECORD +94 -23
- {geek_cafe_saas_sdk-0.6.0.dist-info → geek_cafe_saas_sdk-0.7.1.dist-info}/WHEEL +0 -0
- {geek_cafe_saas_sdk-0.6.0.dist-info → geek_cafe_saas_sdk-0.7.1.dist-info}/licenses/LICENSE +0 -0
geek_cafe_saas_sdk/__init__.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Geek Cafe Services - Base Reusable Services for SaaS
|
|
3
3
|
|
|
4
|
-
Version 0.
|
|
4
|
+
Version 0.6.0 adds File System Service
|
|
5
5
|
"""
|
|
6
|
-
__version__ = "0.
|
|
6
|
+
__version__ = "0.7.1"
|
|
7
7
|
|
|
8
8
|
# Import main modules for easier access
|
|
9
9
|
from . import services
|
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
# File System Lambda Handlers
|
|
2
|
+
|
|
3
|
+
Lambda handlers for the file system with lineage tracking support.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Handler Structure
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
handlers/
|
|
11
|
+
├── files/ # Basic file operations
|
|
12
|
+
│ ├── create/ # Upload file
|
|
13
|
+
│ ├── get/ # Get file metadata
|
|
14
|
+
│ ├── download/ # Download file content
|
|
15
|
+
│ └── list/ # List files
|
|
16
|
+
│
|
|
17
|
+
└── lineage/ # Lineage operations
|
|
18
|
+
├── get_lineage/ # Get file lineage
|
|
19
|
+
├── create_main/ # Create main file from original
|
|
20
|
+
├── create_derived/ # Create derived file from main
|
|
21
|
+
├── prepare_bundle/ # Prepare lineage bundle (metadata only)
|
|
22
|
+
└── download_bundle/# Download complete bundle (with content)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## File Operations
|
|
28
|
+
|
|
29
|
+
### POST /files - Upload File
|
|
30
|
+
|
|
31
|
+
**Handler:** `files/create/app.py`
|
|
32
|
+
|
|
33
|
+
**Body:**
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"fileName": "document.pdf",
|
|
37
|
+
"fileData": "base64_encoded_content",
|
|
38
|
+
"mimeType": "application/pdf",
|
|
39
|
+
"directoryId": "dir-123",
|
|
40
|
+
"versioningStrategy": "explicit",
|
|
41
|
+
"description": "Q1 Report",
|
|
42
|
+
"tags": ["report", "2024"],
|
|
43
|
+
|
|
44
|
+
// Optional lineage fields:
|
|
45
|
+
"fileRole": "original",
|
|
46
|
+
"parentFileId": "file-parent",
|
|
47
|
+
"originalFileId": "file-original",
|
|
48
|
+
"transformationType": "convert",
|
|
49
|
+
"transformationOperation": "xls_to_csv",
|
|
50
|
+
"transformationMetadata": {
|
|
51
|
+
"source_format": "xls",
|
|
52
|
+
"target_format": "csv"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Response:** `201` with file metadata
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
### GET /files/{fileId} - Get File Metadata
|
|
62
|
+
|
|
63
|
+
**Handler:** `files/get/app.py`
|
|
64
|
+
|
|
65
|
+
**Path Parameters:**
|
|
66
|
+
- `fileId` - File ID
|
|
67
|
+
|
|
68
|
+
**Response:** `200` with file metadata including lineage fields
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
### GET /files/{fileId}/download - Download File
|
|
73
|
+
|
|
74
|
+
**Handler:** `files/download/app.py`
|
|
75
|
+
|
|
76
|
+
**Path Parameters:**
|
|
77
|
+
- `fileId` - File ID
|
|
78
|
+
|
|
79
|
+
**Response:** `200` with:
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"file_id": "file-123",
|
|
83
|
+
"file_name": "document.pdf",
|
|
84
|
+
"mime_type": "application/pdf",
|
|
85
|
+
"file_size": 12345,
|
|
86
|
+
"file_data": "base64_encoded_content",
|
|
87
|
+
"content_type": "application/pdf"
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
### GET /files - List Files
|
|
94
|
+
|
|
95
|
+
**Handler:** `files/list/app.py`
|
|
96
|
+
|
|
97
|
+
**Query Parameters:**
|
|
98
|
+
- `directoryId` (optional) - Filter by directory
|
|
99
|
+
- `ownerId` (optional) - Filter by owner (defaults to current user)
|
|
100
|
+
- `limit` (optional) - Max results (default: 100)
|
|
101
|
+
|
|
102
|
+
**Response:** `200` with array of file objects
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Lineage Operations
|
|
107
|
+
|
|
108
|
+
### GET /files/{fileId}/lineage - Get File Lineage
|
|
109
|
+
|
|
110
|
+
**Handler:** `lineage/get_lineage/app.py`
|
|
111
|
+
|
|
112
|
+
**Path Parameters:**
|
|
113
|
+
- `fileId` - File ID
|
|
114
|
+
|
|
115
|
+
**Response:** `200` with:
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"selected": {file object},
|
|
119
|
+
"main": {file object or null},
|
|
120
|
+
"original": {file object or null},
|
|
121
|
+
"allDerived": [{file objects}]
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### POST /files/lineage/main - Create Main File
|
|
128
|
+
|
|
129
|
+
**Handler:** `lineage/create_main/app.py`
|
|
130
|
+
|
|
131
|
+
Convert an original file to a main file (e.g., XLS → CSV).
|
|
132
|
+
|
|
133
|
+
**Body:**
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"originalFileId": "file-123",
|
|
137
|
+
"fileName": "data.csv",
|
|
138
|
+
"fileData": "base64_encoded_content",
|
|
139
|
+
"mimeType": "text/csv",
|
|
140
|
+
"transformationOperation": "xls_to_csv",
|
|
141
|
+
"transformationMetadata": {
|
|
142
|
+
"source_format": "xls",
|
|
143
|
+
"target_format": "csv",
|
|
144
|
+
"converter_version": "1.0"
|
|
145
|
+
},
|
|
146
|
+
"directoryId": "dir-456"
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Response:** `201` with main file metadata
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
### POST /files/lineage/derived - Create Derived File
|
|
155
|
+
|
|
156
|
+
**Handler:** `lineage/create_derived/app.py`
|
|
157
|
+
|
|
158
|
+
Create a derived file from a main file (e.g., data cleaning).
|
|
159
|
+
|
|
160
|
+
**Body:**
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"mainFileId": "file-456",
|
|
164
|
+
"fileName": "data_clean_v1.csv",
|
|
165
|
+
"fileData": "base64_encoded_content",
|
|
166
|
+
"transformationOperation": "data_cleaning_v1",
|
|
167
|
+
"transformationMetadata": {
|
|
168
|
+
"cleaning_version": 1,
|
|
169
|
+
"operations": ["remove_nulls", "normalize_units"],
|
|
170
|
+
"rows_processed": 1000
|
|
171
|
+
},
|
|
172
|
+
"directoryId": "dir-789"
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Response:** `201` with derived file metadata
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
### GET /files/{fileId}/bundle - Prepare Lineage Bundle
|
|
181
|
+
|
|
182
|
+
**Handler:** `lineage/prepare_bundle/app.py`
|
|
183
|
+
|
|
184
|
+
Get bundle metadata without file content (lightweight).
|
|
185
|
+
|
|
186
|
+
**Path Parameters:**
|
|
187
|
+
- `fileId` - File ID
|
|
188
|
+
|
|
189
|
+
**Response:** `200` with:
|
|
190
|
+
```json
|
|
191
|
+
{
|
|
192
|
+
"selectedFile": {file object},
|
|
193
|
+
"mainFile": {file object},
|
|
194
|
+
"originalFile": {file object},
|
|
195
|
+
"metadata": {
|
|
196
|
+
"selectedFileId": "...",
|
|
197
|
+
"selectedFileName": "...",
|
|
198
|
+
"transformationChain": [
|
|
199
|
+
{"step": 1, "type": "original", "fileId": "...", "fileName": "..."},
|
|
200
|
+
{"step": 2, "type": "convert", "fileId": "...", "fileName": "...", "operation": "..."},
|
|
201
|
+
{"step": 3, "type": "clean", "fileId": "...", "fileName": "...", "operation": "..."}
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
### GET /files/{fileId}/bundle/download - Download Lineage Bundle
|
|
210
|
+
|
|
211
|
+
**Handler:** `lineage/download_bundle/app.py`
|
|
212
|
+
|
|
213
|
+
Download complete bundle with file content (heavier).
|
|
214
|
+
|
|
215
|
+
**Path Parameters:**
|
|
216
|
+
- `fileId` - File ID
|
|
217
|
+
|
|
218
|
+
**Response:** `200` with:
|
|
219
|
+
```json
|
|
220
|
+
{
|
|
221
|
+
"selected": {
|
|
222
|
+
"file": {file object},
|
|
223
|
+
"data": "base64_encoded_content"
|
|
224
|
+
},
|
|
225
|
+
"main": {
|
|
226
|
+
"file": {file object},
|
|
227
|
+
"data": "base64_encoded_content"
|
|
228
|
+
},
|
|
229
|
+
"original": {
|
|
230
|
+
"file": {file object},
|
|
231
|
+
"data": "base64_encoded_content"
|
|
232
|
+
},
|
|
233
|
+
"metadata": {transformation chain}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## API Gateway Configuration
|
|
240
|
+
|
|
241
|
+
### Example SAM Template
|
|
242
|
+
|
|
243
|
+
```yaml
|
|
244
|
+
Resources:
|
|
245
|
+
# File Operations
|
|
246
|
+
FileUpload:
|
|
247
|
+
Type: AWS::Serverless::Function
|
|
248
|
+
Properties:
|
|
249
|
+
CodeUri: handlers/files/create/
|
|
250
|
+
Handler: app.handler
|
|
251
|
+
Events:
|
|
252
|
+
ApiEvent:
|
|
253
|
+
Type: Api
|
|
254
|
+
Properties:
|
|
255
|
+
Path: /files
|
|
256
|
+
Method: POST
|
|
257
|
+
Auth:
|
|
258
|
+
Authorizer: CognitoAuthorizer
|
|
259
|
+
|
|
260
|
+
FileGet:
|
|
261
|
+
Type: AWS::Serverless::Function
|
|
262
|
+
Properties:
|
|
263
|
+
CodeUri: handlers/files/get/
|
|
264
|
+
Handler: app.handler
|
|
265
|
+
Events:
|
|
266
|
+
ApiEvent:
|
|
267
|
+
Type: Api
|
|
268
|
+
Properties:
|
|
269
|
+
Path: /files/{fileId}
|
|
270
|
+
Method: GET
|
|
271
|
+
Auth:
|
|
272
|
+
Authorizer: CognitoAuthorizer
|
|
273
|
+
|
|
274
|
+
FileDownload:
|
|
275
|
+
Type: AWS::Serverless::Function
|
|
276
|
+
Properties:
|
|
277
|
+
CodeUri: handlers/files/download/
|
|
278
|
+
Handler: app.handler
|
|
279
|
+
Events:
|
|
280
|
+
ApiEvent:
|
|
281
|
+
Type: Api
|
|
282
|
+
Properties:
|
|
283
|
+
Path: /files/{fileId}/download
|
|
284
|
+
Method: GET
|
|
285
|
+
Auth:
|
|
286
|
+
Authorizer: CognitoAuthorizer
|
|
287
|
+
|
|
288
|
+
FileList:
|
|
289
|
+
Type: AWS::Serverless::Function
|
|
290
|
+
Properties:
|
|
291
|
+
CodeUri: handlers/files/list/
|
|
292
|
+
Handler: app.handler
|
|
293
|
+
Events:
|
|
294
|
+
ApiEvent:
|
|
295
|
+
Type: Api
|
|
296
|
+
Properties:
|
|
297
|
+
Path: /files
|
|
298
|
+
Method: GET
|
|
299
|
+
Auth:
|
|
300
|
+
Authorizer: CognitoAuthorizer
|
|
301
|
+
|
|
302
|
+
# Lineage Operations
|
|
303
|
+
GetLineage:
|
|
304
|
+
Type: AWS::Serverless::Function
|
|
305
|
+
Properties:
|
|
306
|
+
CodeUri: handlers/lineage/get_lineage/
|
|
307
|
+
Handler: app.handler
|
|
308
|
+
Events:
|
|
309
|
+
ApiEvent:
|
|
310
|
+
Type: Api
|
|
311
|
+
Properties:
|
|
312
|
+
Path: /files/{fileId}/lineage
|
|
313
|
+
Method: GET
|
|
314
|
+
Auth:
|
|
315
|
+
Authorizer: CognitoAuthorizer
|
|
316
|
+
|
|
317
|
+
CreateMain:
|
|
318
|
+
Type: AWS::Serverless::Function
|
|
319
|
+
Properties:
|
|
320
|
+
CodeUri: handlers/lineage/create_main/
|
|
321
|
+
Handler: app.handler
|
|
322
|
+
Events:
|
|
323
|
+
ApiEvent:
|
|
324
|
+
Type: Api
|
|
325
|
+
Properties:
|
|
326
|
+
Path: /files/lineage/main
|
|
327
|
+
Method: POST
|
|
328
|
+
Auth:
|
|
329
|
+
Authorizer: CognitoAuthorizer
|
|
330
|
+
|
|
331
|
+
CreateDerived:
|
|
332
|
+
Type: AWS::Serverless::Function
|
|
333
|
+
Properties:
|
|
334
|
+
CodeUri: handlers/lineage/create_derived/
|
|
335
|
+
Handler: app.handler
|
|
336
|
+
Events:
|
|
337
|
+
ApiEvent:
|
|
338
|
+
Type: Api
|
|
339
|
+
Properties:
|
|
340
|
+
Path: /files/lineage/derived
|
|
341
|
+
Method: POST
|
|
342
|
+
Auth:
|
|
343
|
+
Authorizer: CognitoAuthorizer
|
|
344
|
+
|
|
345
|
+
PrepareBundle:
|
|
346
|
+
Type: AWS::Serverless::Function
|
|
347
|
+
Properties:
|
|
348
|
+
CodeUri: handlers/lineage/prepare_bundle/
|
|
349
|
+
Handler: app.handler
|
|
350
|
+
Events:
|
|
351
|
+
ApiEvent:
|
|
352
|
+
Type: Api
|
|
353
|
+
Properties:
|
|
354
|
+
Path: /files/{fileId}/bundle
|
|
355
|
+
Method: GET
|
|
356
|
+
Auth:
|
|
357
|
+
Authorizer: CognitoAuthorizer
|
|
358
|
+
|
|
359
|
+
DownloadBundle:
|
|
360
|
+
Type: AWS::Serverless::Function
|
|
361
|
+
Properties:
|
|
362
|
+
CodeUri: handlers/lineage/download_bundle/
|
|
363
|
+
Handler: app.handler
|
|
364
|
+
Events:
|
|
365
|
+
ApiEvent:
|
|
366
|
+
Type: Api
|
|
367
|
+
Properties:
|
|
368
|
+
Path: /files/{fileId}/bundle/download
|
|
369
|
+
Method: GET
|
|
370
|
+
Auth:
|
|
371
|
+
Authorizer: CognitoAuthorizer
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## Authentication
|
|
377
|
+
|
|
378
|
+
All handlers require secure authentication (Cognito JWT tokens).
|
|
379
|
+
|
|
380
|
+
**Required User Context:**
|
|
381
|
+
- `user_id` - User performing the action
|
|
382
|
+
- `tenant_id` - Organization/tenant ID
|
|
383
|
+
- `email` - User email (optional)
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Error Responses
|
|
388
|
+
|
|
389
|
+
All handlers return standard error responses:
|
|
390
|
+
|
|
391
|
+
```json
|
|
392
|
+
{
|
|
393
|
+
"error": {
|
|
394
|
+
"code": "VALIDATION_ERROR",
|
|
395
|
+
"message": "file_name is required",
|
|
396
|
+
"details": {"field": "file_name"}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
**Common Error Codes:**
|
|
402
|
+
- `VALIDATION_ERROR` - Invalid input
|
|
403
|
+
- `NOT_FOUND` - File not found
|
|
404
|
+
- `ACCESS_DENIED` - Insufficient permissions
|
|
405
|
+
- `INVALID_FILE_ROLE` - Lineage role mismatch
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## Testing
|
|
410
|
+
|
|
411
|
+
Each handler can be tested independently:
|
|
412
|
+
|
|
413
|
+
```python
|
|
414
|
+
from handlers.files.create import app
|
|
415
|
+
|
|
416
|
+
# Mock event
|
|
417
|
+
event = {
|
|
418
|
+
"body": json.dumps({
|
|
419
|
+
"fileName": "test.txt",
|
|
420
|
+
"fileData": base64.b64encode(b"test").decode(),
|
|
421
|
+
"mimeType": "text/plain"
|
|
422
|
+
}),
|
|
423
|
+
"requestContext": {
|
|
424
|
+
"authorizer": {
|
|
425
|
+
"claims": {
|
|
426
|
+
"sub": "user-123",
|
|
427
|
+
"custom:tenant_id": "tenant-456"
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
# Test
|
|
434
|
+
result = app.handler(event, None)
|
|
435
|
+
print(result)
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
## Next Steps
|
|
441
|
+
|
|
442
|
+
1. Deploy handlers using SAM/CloudFormation
|
|
443
|
+
2. Configure API Gateway routes
|
|
444
|
+
3. Set up Cognito authorizer
|
|
445
|
+
4. Test each endpoint
|
|
446
|
+
5. Monitor CloudWatch logs
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Lambda handler for creating/uploading files.
|
|
3
|
+
|
|
4
|
+
Requires authentication (secure mode).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Dict, Any
|
|
8
|
+
from geek_cafe_saas_sdk.lambda_handlers import create_handler
|
|
9
|
+
from geek_cafe_saas_sdk.domains.files.services import FileSystemService
|
|
10
|
+
import base64
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Factory creates handler (defaults to secure auth)
|
|
14
|
+
handler_wrapper = create_handler(
|
|
15
|
+
service_class=FileSystemService,
|
|
16
|
+
require_body=True,
|
|
17
|
+
convert_case=True
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def handler(event: Dict[str, Any], context: Any, injected_service=None) -> Dict[str, Any]:
|
|
22
|
+
"""
|
|
23
|
+
Upload a new file.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
event: Lambda event from API Gateway
|
|
27
|
+
context: Lambda context
|
|
28
|
+
injected_service: Optional FileSystemService for testing
|
|
29
|
+
|
|
30
|
+
Expected body (camelCase from frontend):
|
|
31
|
+
{
|
|
32
|
+
"fileName": "document.pdf",
|
|
33
|
+
"fileData": "base64_encoded_content",
|
|
34
|
+
"mimeType": "application/pdf",
|
|
35
|
+
"directoryId": "dir-123", # Optional
|
|
36
|
+
"versioningStrategy": "explicit", # Optional: "s3_native" or "explicit"
|
|
37
|
+
"description": "Q1 Report", # Optional
|
|
38
|
+
"tags": ["report", "2024"], # Optional
|
|
39
|
+
|
|
40
|
+
# Optional lineage fields:
|
|
41
|
+
"fileRole": "original", # "standalone", "original", "main", "derived"
|
|
42
|
+
"parentFileId": "file-parent", # For lineage tracking
|
|
43
|
+
"originalFileId": "file-original", # For lineage tracking
|
|
44
|
+
"transformationType": "convert", # "convert", "clean", "process"
|
|
45
|
+
"transformationOperation": "xls_to_csv", # Operation name
|
|
46
|
+
"transformationMetadata": { # Operation details
|
|
47
|
+
"source_format": "xls",
|
|
48
|
+
"target_format": "csv"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
Returns 201 with created file metadata
|
|
53
|
+
"""
|
|
54
|
+
return handler_wrapper.execute(event, context, upload_file, injected_service)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def upload_file(
|
|
58
|
+
event: Dict[str, Any],
|
|
59
|
+
service: FileSystemService,
|
|
60
|
+
user_context: Dict[str, str]
|
|
61
|
+
) -> Any:
|
|
62
|
+
"""
|
|
63
|
+
Business logic for uploading files.
|
|
64
|
+
"""
|
|
65
|
+
payload = event["parsed_body"]
|
|
66
|
+
|
|
67
|
+
tenant_id = user_context.get("tenant_id")
|
|
68
|
+
user_id = user_context.get("user_id")
|
|
69
|
+
|
|
70
|
+
# Extract required fields
|
|
71
|
+
file_name = payload.get("file_name")
|
|
72
|
+
file_data_b64 = payload.get("file_data")
|
|
73
|
+
mime_type = payload.get("mime_type")
|
|
74
|
+
|
|
75
|
+
if not file_name:
|
|
76
|
+
raise ValueError("file_name is required")
|
|
77
|
+
if not file_data_b64:
|
|
78
|
+
raise ValueError("file_data is required")
|
|
79
|
+
if not mime_type:
|
|
80
|
+
raise ValueError("mime_type is required")
|
|
81
|
+
|
|
82
|
+
# Decode base64 file data
|
|
83
|
+
try:
|
|
84
|
+
file_data = base64.b64decode(file_data_b64)
|
|
85
|
+
except Exception as e:
|
|
86
|
+
raise ValueError(f"Invalid base64 file_data: {str(e)}")
|
|
87
|
+
|
|
88
|
+
# Extract optional fields
|
|
89
|
+
directory_id = payload.get("directory_id")
|
|
90
|
+
versioning_strategy = payload.get("versioning_strategy", "explicit")
|
|
91
|
+
description = payload.get("description")
|
|
92
|
+
tags = payload.get("tags", [])
|
|
93
|
+
|
|
94
|
+
# Extract lineage fields
|
|
95
|
+
file_role = payload.get("file_role")
|
|
96
|
+
parent_file_id = payload.get("parent_file_id")
|
|
97
|
+
original_file_id = payload.get("original_file_id")
|
|
98
|
+
transformation_type = payload.get("transformation_type")
|
|
99
|
+
transformation_operation = payload.get("transformation_operation")
|
|
100
|
+
transformation_metadata = payload.get("transformation_metadata")
|
|
101
|
+
|
|
102
|
+
# Upload file
|
|
103
|
+
result = service.create(
|
|
104
|
+
tenant_id=tenant_id,
|
|
105
|
+
user_id=user_id,
|
|
106
|
+
file_name=file_name,
|
|
107
|
+
file_data=file_data,
|
|
108
|
+
mime_type=mime_type,
|
|
109
|
+
directory_id=directory_id,
|
|
110
|
+
versioning_strategy=versioning_strategy,
|
|
111
|
+
description=description,
|
|
112
|
+
tags=tags,
|
|
113
|
+
file_role=file_role,
|
|
114
|
+
parent_file_id=parent_file_id,
|
|
115
|
+
original_file_id=original_file_id,
|
|
116
|
+
transformation_type=transformation_type,
|
|
117
|
+
transformation_operation=transformation_operation,
|
|
118
|
+
transformation_metadata=transformation_metadata
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
return result
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Lambda handler for downloading file content.
|
|
3
|
+
|
|
4
|
+
Requires authentication (secure mode).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Dict, Any
|
|
8
|
+
from geek_cafe_saas_sdk.lambda_handlers import create_handler
|
|
9
|
+
from geek_cafe_saas_sdk.domains.files.services import FileSystemService
|
|
10
|
+
import base64
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Factory creates handler (defaults to secure auth)
|
|
14
|
+
handler_wrapper = create_handler(
|
|
15
|
+
service_class=FileSystemService,
|
|
16
|
+
require_body=False,
|
|
17
|
+
convert_case=True
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def handler(event: Dict[str, Any], context: Any, injected_service=None) -> Dict[str, Any]:
|
|
22
|
+
"""
|
|
23
|
+
Download file content.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
event: Lambda event from API Gateway
|
|
27
|
+
context: Lambda context
|
|
28
|
+
injected_service: Optional FileSystemService for testing
|
|
29
|
+
|
|
30
|
+
Path parameters:
|
|
31
|
+
fileId: File ID
|
|
32
|
+
|
|
33
|
+
Returns 200 with file content (base64 encoded for binary files)
|
|
34
|
+
"""
|
|
35
|
+
return handler_wrapper.execute(event, context, download_file, injected_service)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def download_file(
|
|
39
|
+
event: Dict[str, Any],
|
|
40
|
+
service: FileSystemService,
|
|
41
|
+
user_context: Dict[str, str]
|
|
42
|
+
) -> Dict[str, Any]:
|
|
43
|
+
"""
|
|
44
|
+
Business logic for downloading file content.
|
|
45
|
+
"""
|
|
46
|
+
tenant_id = user_context.get("tenant_id")
|
|
47
|
+
user_id = user_context.get("user_id")
|
|
48
|
+
|
|
49
|
+
# Get file ID from path parameters
|
|
50
|
+
path_params = event.get("pathParameters", {})
|
|
51
|
+
file_id = path_params.get("fileId") or path_params.get("id")
|
|
52
|
+
|
|
53
|
+
if not file_id:
|
|
54
|
+
raise ValueError("fileId path parameter is required")
|
|
55
|
+
|
|
56
|
+
# Download file
|
|
57
|
+
result = service.download_file(
|
|
58
|
+
tenant_id=tenant_id,
|
|
59
|
+
file_id=file_id,
|
|
60
|
+
user_id=user_id
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if result.success:
|
|
64
|
+
file_data = result.data['data']
|
|
65
|
+
file_info = result.data['file']
|
|
66
|
+
|
|
67
|
+
# Encode binary data as base64 for JSON response
|
|
68
|
+
encoded_data = base64.b64encode(file_data).decode('utf-8')
|
|
69
|
+
|
|
70
|
+
# Return with metadata
|
|
71
|
+
return {
|
|
72
|
+
'file_id': file_info.file_id,
|
|
73
|
+
'file_name': file_info.file_name,
|
|
74
|
+
'mime_type': file_info.mime_type,
|
|
75
|
+
'file_size': file_info.file_size,
|
|
76
|
+
'file_data': encoded_data,
|
|
77
|
+
'content_type': file_info.mime_type
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return result
|