geek-cafe-saas-sdk 0.6.0__py3-none-any.whl → 0.7.0__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.

Files changed (22) hide show
  1. geek_cafe_saas_sdk/__init__.py +2 -2
  2. geek_cafe_saas_sdk/domains/files/handlers/README.md +446 -0
  3. geek_cafe_saas_sdk/domains/files/handlers/__init__.py +6 -0
  4. geek_cafe_saas_sdk/domains/files/handlers/files/create/app.py +121 -0
  5. geek_cafe_saas_sdk/domains/files/handlers/files/download/app.py +80 -0
  6. geek_cafe_saas_sdk/domains/files/handlers/files/get/app.py +62 -0
  7. geek_cafe_saas_sdk/domains/files/handlers/files/list/app.py +72 -0
  8. geek_cafe_saas_sdk/domains/files/handlers/lineage/create_derived/app.py +99 -0
  9. geek_cafe_saas_sdk/domains/files/handlers/lineage/create_main/app.py +104 -0
  10. geek_cafe_saas_sdk/domains/files/handlers/lineage/download_bundle/app.py +99 -0
  11. geek_cafe_saas_sdk/domains/files/handlers/lineage/get_lineage/app.py +68 -0
  12. geek_cafe_saas_sdk/domains/files/handlers/lineage/prepare_bundle/app.py +76 -0
  13. geek_cafe_saas_sdk/domains/files/models/__init__.py +17 -0
  14. geek_cafe_saas_sdk/domains/files/models/file.py +118 -12
  15. geek_cafe_saas_sdk/domains/files/services/__init__.py +21 -0
  16. geek_cafe_saas_sdk/domains/files/services/file_lineage_service.py +487 -0
  17. geek_cafe_saas_sdk/domains/files/services/file_system_service.py +27 -1
  18. geek_cafe_saas_sdk/utilities/cognito_utility.py +16 -26
  19. {geek_cafe_saas_sdk-0.6.0.dist-info → geek_cafe_saas_sdk-0.7.0.dist-info}/METADATA +11 -11
  20. {geek_cafe_saas_sdk-0.6.0.dist-info → geek_cafe_saas_sdk-0.7.0.dist-info}/RECORD +22 -10
  21. {geek_cafe_saas_sdk-0.6.0.dist-info → geek_cafe_saas_sdk-0.7.0.dist-info}/WHEEL +0 -0
  22. {geek_cafe_saas_sdk-0.6.0.dist-info → geek_cafe_saas_sdk-0.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,62 @@
1
+ """
2
+ Lambda handler for getting file metadata.
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
+
11
+
12
+ # Factory creates handler (defaults to secure auth)
13
+ handler_wrapper = create_handler(
14
+ service_class=FileSystemService,
15
+ require_body=False,
16
+ convert_case=True
17
+ )
18
+
19
+
20
+ def handler(event: Dict[str, Any], context: Any, injected_service=None) -> Dict[str, Any]:
21
+ """
22
+ Get file metadata by ID.
23
+
24
+ Args:
25
+ event: Lambda event from API Gateway
26
+ context: Lambda context
27
+ injected_service: Optional FileSystemService for testing
28
+
29
+ Path parameters:
30
+ fileId: File ID
31
+
32
+ Returns 200 with file metadata
33
+ """
34
+ return handler_wrapper.execute(event, context, get_file, injected_service)
35
+
36
+
37
+ def get_file(
38
+ event: Dict[str, Any],
39
+ service: FileSystemService,
40
+ user_context: Dict[str, str]
41
+ ) -> Any:
42
+ """
43
+ Business logic for getting file metadata.
44
+ """
45
+ tenant_id = user_context.get("tenant_id")
46
+ user_id = user_context.get("user_id")
47
+
48
+ # Get file ID from path parameters
49
+ path_params = event.get("pathParameters", {})
50
+ file_id = path_params.get("fileId") or path_params.get("id")
51
+
52
+ if not file_id:
53
+ raise ValueError("fileId path parameter is required")
54
+
55
+ # Get file metadata
56
+ result = service.get_by_id(
57
+ resource_id=file_id,
58
+ tenant_id=tenant_id,
59
+ user_id=user_id
60
+ )
61
+
62
+ return result
@@ -0,0 +1,72 @@
1
+ """
2
+ Lambda handler for listing 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
+
11
+
12
+ # Factory creates handler (defaults to secure auth)
13
+ handler_wrapper = create_handler(
14
+ service_class=FileSystemService,
15
+ require_body=False,
16
+ convert_case=True
17
+ )
18
+
19
+
20
+ def handler(event: Dict[str, Any], context: Any, injected_service=None) -> Dict[str, Any]:
21
+ """
22
+ List files.
23
+
24
+ Args:
25
+ event: Lambda event from API Gateway
26
+ context: Lambda context
27
+ injected_service: Optional FileSystemService for testing
28
+
29
+ Query parameters:
30
+ directoryId: Filter by directory (optional)
31
+ ownerId: Filter by owner (optional)
32
+ limit: Max results (optional, default: 100)
33
+
34
+ Returns 200 with list of files
35
+ """
36
+ return handler_wrapper.execute(event, context, list_files, injected_service)
37
+
38
+
39
+ def list_files(
40
+ event: Dict[str, Any],
41
+ service: FileSystemService,
42
+ user_context: Dict[str, str]
43
+ ) -> Any:
44
+ """
45
+ Business logic for listing files.
46
+ """
47
+ tenant_id = user_context.get("tenant_id")
48
+ user_id = user_context.get("user_id")
49
+
50
+ # Get query parameters
51
+ query_params = event.get("queryStringParameters", {}) or {}
52
+ directory_id = query_params.get("directoryId")
53
+ owner_id = query_params.get("ownerId") or user_id # Default to current user
54
+ limit = int(query_params.get("limit", "100"))
55
+
56
+ # List files by directory or owner
57
+ if directory_id:
58
+ result = service.list_files_by_directory(
59
+ tenant_id=tenant_id,
60
+ directory_id=directory_id,
61
+ user_id=user_id,
62
+ limit=limit
63
+ )
64
+ else:
65
+ result = service.list_files_by_owner(
66
+ tenant_id=tenant_id,
67
+ owner_id=owner_id,
68
+ user_id=user_id,
69
+ limit=limit
70
+ )
71
+
72
+ return result
@@ -0,0 +1,99 @@
1
+ """
2
+ Lambda handler for creating a derived file from a main file.
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 FileLineageService
10
+ import base64
11
+
12
+
13
+ # Factory creates handler (defaults to secure auth)
14
+ handler_wrapper = create_handler(
15
+ service_class=FileLineageService,
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
+ Create a derived file from a main file (e.g., data cleaning).
24
+
25
+ Args:
26
+ event: Lambda event from API Gateway
27
+ context: Lambda context
28
+ injected_service: Optional FileLineageService for testing
29
+
30
+ Expected body (camelCase from frontend):
31
+ {
32
+ "mainFileId": "file-456", # Main file ID
33
+ "fileName": "data_clean_v1.csv",
34
+ "fileData": "base64_encoded_content",
35
+ "transformationOperation": "data_cleaning_v1",
36
+ "transformationMetadata": {
37
+ "cleaning_version": 1,
38
+ "operations": ["remove_nulls", "normalize_units"],
39
+ "rows_processed": 1000
40
+ },
41
+ "directoryId": "dir-789" # Optional
42
+ }
43
+
44
+ Returns 201 with created derived file
45
+ """
46
+ return handler_wrapper.execute(event, context, create_derived_file, injected_service)
47
+
48
+
49
+ def create_derived_file(
50
+ event: Dict[str, Any],
51
+ service: FileLineageService,
52
+ user_context: Dict[str, str]
53
+ ) -> Any:
54
+ """
55
+ Business logic for creating derived file.
56
+ """
57
+ payload = event["parsed_body"]
58
+
59
+ tenant_id = user_context.get("tenant_id")
60
+ user_id = user_context.get("user_id")
61
+
62
+ # Extract required fields
63
+ main_file_id = payload.get("main_file_id")
64
+ file_name = payload.get("file_name")
65
+ file_data_b64 = payload.get("file_data")
66
+ transformation_operation = payload.get("transformation_operation")
67
+
68
+ if not main_file_id:
69
+ raise ValueError("main_file_id is required")
70
+ if not file_name:
71
+ raise ValueError("file_name is required")
72
+ if not file_data_b64:
73
+ raise ValueError("file_data is required")
74
+ if not transformation_operation:
75
+ raise ValueError("transformation_operation is required")
76
+
77
+ # Decode base64 file data
78
+ try:
79
+ file_data = base64.b64decode(file_data_b64)
80
+ except Exception as e:
81
+ raise ValueError(f"Invalid base64 file_data: {str(e)}")
82
+
83
+ # Extract optional fields
84
+ transformation_metadata = payload.get("transformation_metadata", {})
85
+ directory_id = payload.get("directory_id")
86
+
87
+ # Create derived file
88
+ result = service.create_derived_file(
89
+ tenant_id=tenant_id,
90
+ user_id=user_id,
91
+ main_file_id=main_file_id,
92
+ file_name=file_name,
93
+ file_data=file_data,
94
+ transformation_operation=transformation_operation,
95
+ transformation_metadata=transformation_metadata,
96
+ directory_id=directory_id
97
+ )
98
+
99
+ return result
@@ -0,0 +1,104 @@
1
+ """
2
+ Lambda handler for creating a main file from an original file.
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 FileLineageService
10
+ import base64
11
+
12
+
13
+ # Factory creates handler (defaults to secure auth)
14
+ handler_wrapper = create_handler(
15
+ service_class=FileLineageService,
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
+ Create a main file from an original file (e.g., XLS → CSV conversion).
24
+
25
+ Args:
26
+ event: Lambda event from API Gateway
27
+ context: Lambda context
28
+ injected_service: Optional FileLineageService for testing
29
+
30
+ Expected body (camelCase from frontend):
31
+ {
32
+ "originalFileId": "file-123", # Original file ID
33
+ "fileName": "data.csv",
34
+ "fileData": "base64_encoded_content",
35
+ "mimeType": "text/csv",
36
+ "transformationOperation": "xls_to_csv",
37
+ "transformationMetadata": {
38
+ "source_format": "xls",
39
+ "target_format": "csv",
40
+ "converter_version": "1.0"
41
+ },
42
+ "directoryId": "dir-456" # Optional
43
+ }
44
+
45
+ Returns 201 with created main file
46
+ """
47
+ return handler_wrapper.execute(event, context, create_main_file, injected_service)
48
+
49
+
50
+ def create_main_file(
51
+ event: Dict[str, Any],
52
+ service: FileLineageService,
53
+ user_context: Dict[str, str]
54
+ ) -> Any:
55
+ """
56
+ Business logic for creating main file.
57
+ """
58
+ payload = event["parsed_body"]
59
+
60
+ tenant_id = user_context.get("tenant_id")
61
+ user_id = user_context.get("user_id")
62
+
63
+ # Extract required fields
64
+ original_file_id = payload.get("original_file_id")
65
+ file_name = payload.get("file_name")
66
+ file_data_b64 = payload.get("file_data")
67
+ mime_type = payload.get("mime_type")
68
+ transformation_operation = payload.get("transformation_operation")
69
+
70
+ if not original_file_id:
71
+ raise ValueError("original_file_id is required")
72
+ if not file_name:
73
+ raise ValueError("file_name is required")
74
+ if not file_data_b64:
75
+ raise ValueError("file_data is required")
76
+ if not mime_type:
77
+ raise ValueError("mime_type is required")
78
+ if not transformation_operation:
79
+ raise ValueError("transformation_operation is required")
80
+
81
+ # Decode base64 file data
82
+ try:
83
+ file_data = base64.b64decode(file_data_b64)
84
+ except Exception as e:
85
+ raise ValueError(f"Invalid base64 file_data: {str(e)}")
86
+
87
+ # Extract optional fields
88
+ transformation_metadata = payload.get("transformation_metadata", {})
89
+ directory_id = payload.get("directory_id")
90
+
91
+ # Create main file
92
+ result = service.create_main_file(
93
+ tenant_id=tenant_id,
94
+ user_id=user_id,
95
+ original_file_id=original_file_id,
96
+ file_name=file_name,
97
+ file_data=file_data,
98
+ mime_type=mime_type,
99
+ transformation_operation=transformation_operation,
100
+ transformation_metadata=transformation_metadata,
101
+ directory_id=directory_id
102
+ )
103
+
104
+ return result
@@ -0,0 +1,99 @@
1
+ """
2
+ Lambda handler for downloading complete lineage bundle with 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 FileLineageService
10
+ import base64
11
+
12
+
13
+ # Factory creates handler (defaults to secure auth)
14
+ handler_wrapper = create_handler(
15
+ service_class=FileLineageService,
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 complete lineage bundle with file content.
24
+
25
+ Args:
26
+ event: Lambda event from API Gateway
27
+ context: Lambda context
28
+ injected_service: Optional FileLineageService for testing
29
+
30
+ Path parameters:
31
+ fileId: File ID to bundle
32
+
33
+ Returns 200 with bundle including file data (base64 encoded):
34
+ {
35
+ "selected": {
36
+ "file": {file object},
37
+ "data": "base64_encoded_content"
38
+ },
39
+ "main": {
40
+ "file": {file object},
41
+ "data": "base64_encoded_content"
42
+ },
43
+ "original": {
44
+ "file": {file object},
45
+ "data": "base64_encoded_content"
46
+ },
47
+ "metadata": {transformation chain}
48
+ }
49
+ """
50
+ return handler_wrapper.execute(event, context, download_bundle, injected_service)
51
+
52
+
53
+ def download_bundle(
54
+ event: Dict[str, Any],
55
+ service: FileLineageService,
56
+ user_context: Dict[str, str]
57
+ ) -> Dict[str, Any]:
58
+ """
59
+ Business logic for downloading lineage bundle.
60
+ """
61
+ tenant_id = user_context.get("tenant_id")
62
+ user_id = user_context.get("user_id")
63
+
64
+ # Get file ID from path parameters
65
+ path_params = event.get("pathParameters", {})
66
+ file_id = path_params.get("fileId") or path_params.get("id")
67
+
68
+ if not file_id:
69
+ raise ValueError("fileId path parameter is required")
70
+
71
+ # Download bundle
72
+ result = service.download_lineage_bundle(
73
+ selected_file_id=file_id,
74
+ tenant_id=tenant_id,
75
+ user_id=user_id
76
+ )
77
+
78
+ if result.success:
79
+ bundle = result.data
80
+
81
+ # Encode file data as base64 for JSON response
82
+ if bundle.get('selected') and bundle['selected'].get('data'):
83
+ bundle['selected']['data'] = base64.b64encode(
84
+ bundle['selected']['data']
85
+ ).decode('utf-8')
86
+
87
+ if bundle.get('main') and bundle['main'].get('data'):
88
+ bundle['main']['data'] = base64.b64encode(
89
+ bundle['main']['data']
90
+ ).decode('utf-8')
91
+
92
+ if bundle.get('original') and bundle['original'].get('data'):
93
+ bundle['original']['data'] = base64.b64encode(
94
+ bundle['original']['data']
95
+ ).decode('utf-8')
96
+
97
+ return bundle
98
+
99
+ return result
@@ -0,0 +1,68 @@
1
+ """
2
+ Lambda handler for getting file lineage.
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 FileLineageService
10
+
11
+
12
+ # Factory creates handler (defaults to secure auth)
13
+ handler_wrapper = create_handler(
14
+ service_class=FileLineageService,
15
+ require_body=False,
16
+ convert_case=True
17
+ )
18
+
19
+
20
+ def handler(event: Dict[str, Any], context: Any, injected_service=None) -> Dict[str, Any]:
21
+ """
22
+ Get complete lineage for a file.
23
+
24
+ Args:
25
+ event: Lambda event from API Gateway
26
+ context: Lambda context
27
+ injected_service: Optional FileLineageService for testing
28
+
29
+ Path parameters:
30
+ fileId: File ID
31
+
32
+ Returns 200 with lineage information:
33
+ {
34
+ "selected": {file object},
35
+ "main": {file object or null},
36
+ "original": {file object or null},
37
+ "allDerived": [{file objects}] # If viewing main file
38
+ }
39
+ """
40
+ return handler_wrapper.execute(event, context, get_file_lineage, injected_service)
41
+
42
+
43
+ def get_file_lineage(
44
+ event: Dict[str, Any],
45
+ service: FileLineageService,
46
+ user_context: Dict[str, str]
47
+ ) -> Any:
48
+ """
49
+ Business logic for getting file lineage.
50
+ """
51
+ tenant_id = user_context.get("tenant_id")
52
+ user_id = user_context.get("user_id")
53
+
54
+ # Get file ID from path parameters
55
+ path_params = event.get("pathParameters", {})
56
+ file_id = path_params.get("fileId") or path_params.get("id")
57
+
58
+ if not file_id:
59
+ raise ValueError("fileId path parameter is required")
60
+
61
+ # Get lineage
62
+ result = service.get_lineage(
63
+ file_id=file_id,
64
+ tenant_id=tenant_id,
65
+ user_id=user_id
66
+ )
67
+
68
+ return result
@@ -0,0 +1,76 @@
1
+ """
2
+ Lambda handler for preparing lineage bundle.
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 FileLineageService
10
+
11
+
12
+ # Factory creates handler (defaults to secure auth)
13
+ handler_wrapper = create_handler(
14
+ service_class=FileLineageService,
15
+ require_body=False,
16
+ convert_case=True
17
+ )
18
+
19
+
20
+ def handler(event: Dict[str, Any], context: Any, injected_service=None) -> Dict[str, Any]:
21
+ """
22
+ Prepare lineage bundle for a file (metadata only, no file content).
23
+
24
+ Args:
25
+ event: Lambda event from API Gateway
26
+ context: Lambda context
27
+ injected_service: Optional FileLineageService for testing
28
+
29
+ Path parameters:
30
+ fileId: File ID to bundle
31
+
32
+ Returns 200 with bundle information:
33
+ {
34
+ "selectedFile": {file object},
35
+ "mainFile": {file object or null},
36
+ "originalFile": {file object or null},
37
+ "metadata": {
38
+ "selectedFileId": "...",
39
+ "selectedFileName": "...",
40
+ "transformationChain": [
41
+ {"step": 1, "type": "original", "fileId": "...", "fileName": "..."},
42
+ {"step": 2, "type": "convert", "fileId": "...", "fileName": "...", "operation": "..."},
43
+ {"step": 3, "type": "clean", "fileId": "...", "fileName": "...", "operation": "..."}
44
+ ]
45
+ }
46
+ }
47
+ """
48
+ return handler_wrapper.execute(event, context, prepare_bundle, injected_service)
49
+
50
+
51
+ def prepare_bundle(
52
+ event: Dict[str, Any],
53
+ service: FileLineageService,
54
+ user_context: Dict[str, str]
55
+ ) -> Any:
56
+ """
57
+ Business logic for preparing lineage bundle.
58
+ """
59
+ tenant_id = user_context.get("tenant_id")
60
+ user_id = user_context.get("user_id")
61
+
62
+ # Get file ID from path parameters
63
+ path_params = event.get("pathParameters", {})
64
+ file_id = path_params.get("fileId") or path_params.get("id")
65
+
66
+ if not file_id:
67
+ raise ValueError("fileId path parameter is required")
68
+
69
+ # Prepare bundle
70
+ result = service.prepare_lineage_bundle(
71
+ selected_file_id=file_id,
72
+ tenant_id=tenant_id,
73
+ user_id=user_id
74
+ )
75
+
76
+ return result
@@ -0,0 +1,17 @@
1
+ """File models.
2
+
3
+ Geek Cafe, LLC
4
+ MIT License. See Project Root for the license information.
5
+ """
6
+
7
+ from .file import File
8
+ from .directory import Directory
9
+ from .file_version import FileVersion
10
+ from .file_share import FileShare
11
+
12
+ __all__ = [
13
+ "File",
14
+ "Directory",
15
+ "FileVersion",
16
+ "FileShare",
17
+ ]