geek-cafe-saas-sdk 0.6.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 (194) hide show
  1. geek_cafe_saas_sdk/__init__.py +9 -0
  2. geek_cafe_saas_sdk/core/__init__.py +11 -0
  3. geek_cafe_saas_sdk/core/audit_mixin.py +33 -0
  4. geek_cafe_saas_sdk/core/error_codes.py +132 -0
  5. geek_cafe_saas_sdk/core/service_errors.py +19 -0
  6. geek_cafe_saas_sdk/core/service_result.py +121 -0
  7. geek_cafe_saas_sdk/decorators/__init__.py +64 -0
  8. geek_cafe_saas_sdk/decorators/auth.py +373 -0
  9. geek_cafe_saas_sdk/decorators/core.py +358 -0
  10. geek_cafe_saas_sdk/domains/__init__.py +0 -0
  11. geek_cafe_saas_sdk/domains/analytics/__init__.py +0 -0
  12. geek_cafe_saas_sdk/domains/analytics/handlers/__init__.py +0 -0
  13. geek_cafe_saas_sdk/domains/analytics/models/__init__.py +9 -0
  14. geek_cafe_saas_sdk/domains/analytics/models/website_analytics.py +219 -0
  15. geek_cafe_saas_sdk/domains/analytics/models/website_analytics_summary.py +220 -0
  16. geek_cafe_saas_sdk/domains/analytics/services/__init__.py +11 -0
  17. geek_cafe_saas_sdk/domains/analytics/services/website_analytics_service.py +232 -0
  18. geek_cafe_saas_sdk/domains/analytics/services/website_analytics_summary_service.py +212 -0
  19. geek_cafe_saas_sdk/domains/analytics/services/website_analytics_tally_service.py +610 -0
  20. geek_cafe_saas_sdk/domains/auth/__init__.py +0 -0
  21. geek_cafe_saas_sdk/domains/auth/handlers/__init__.py +0 -0
  22. geek_cafe_saas_sdk/domains/auth/handlers/users/create/app.py +41 -0
  23. geek_cafe_saas_sdk/domains/auth/handlers/users/delete/app.py +41 -0
  24. geek_cafe_saas_sdk/domains/auth/handlers/users/get/app.py +39 -0
  25. geek_cafe_saas_sdk/domains/auth/handlers/users/list/app.py +36 -0
  26. geek_cafe_saas_sdk/domains/auth/handlers/users/update/app.py +44 -0
  27. geek_cafe_saas_sdk/domains/auth/models/__init__.py +13 -0
  28. geek_cafe_saas_sdk/domains/auth/models/permission.py +134 -0
  29. geek_cafe_saas_sdk/domains/auth/models/resource_permission.py +245 -0
  30. geek_cafe_saas_sdk/domains/auth/models/role.py +213 -0
  31. geek_cafe_saas_sdk/domains/auth/models/user.py +285 -0
  32. geek_cafe_saas_sdk/domains/auth/services/__init__.py +16 -0
  33. geek_cafe_saas_sdk/domains/auth/services/authorization_service.py +376 -0
  34. geek_cafe_saas_sdk/domains/auth/services/permission_registry.py +464 -0
  35. geek_cafe_saas_sdk/domains/auth/services/resource_permission_service.py +408 -0
  36. geek_cafe_saas_sdk/domains/auth/services/user_service.py +274 -0
  37. geek_cafe_saas_sdk/domains/communities/__init__.py +0 -0
  38. geek_cafe_saas_sdk/domains/communities/handlers/__init__.py +0 -0
  39. geek_cafe_saas_sdk/domains/communities/handlers/communities/create/app.py +41 -0
  40. geek_cafe_saas_sdk/domains/communities/handlers/communities/delete/app.py +41 -0
  41. geek_cafe_saas_sdk/domains/communities/handlers/communities/get/app.py +39 -0
  42. geek_cafe_saas_sdk/domains/communities/handlers/communities/list/app.py +36 -0
  43. geek_cafe_saas_sdk/domains/communities/handlers/communities/update/app.py +44 -0
  44. geek_cafe_saas_sdk/domains/communities/models/__init__.py +6 -0
  45. geek_cafe_saas_sdk/domains/communities/models/community.py +326 -0
  46. geek_cafe_saas_sdk/domains/communities/models/community_member.py +227 -0
  47. geek_cafe_saas_sdk/domains/communities/services/__init__.py +6 -0
  48. geek_cafe_saas_sdk/domains/communities/services/community_member_service.py +412 -0
  49. geek_cafe_saas_sdk/domains/communities/services/community_service.py +479 -0
  50. geek_cafe_saas_sdk/domains/events/__init__.py +0 -0
  51. geek_cafe_saas_sdk/domains/events/handlers/__init__.py +0 -0
  52. geek_cafe_saas_sdk/domains/events/handlers/attendees/app.py +67 -0
  53. geek_cafe_saas_sdk/domains/events/handlers/cancel/app.py +66 -0
  54. geek_cafe_saas_sdk/domains/events/handlers/check_in/app.py +60 -0
  55. geek_cafe_saas_sdk/domains/events/handlers/create/app.py +93 -0
  56. geek_cafe_saas_sdk/domains/events/handlers/delete/app.py +42 -0
  57. geek_cafe_saas_sdk/domains/events/handlers/get/app.py +39 -0
  58. geek_cafe_saas_sdk/domains/events/handlers/invite/app.py +98 -0
  59. geek_cafe_saas_sdk/domains/events/handlers/list/app.py +125 -0
  60. geek_cafe_saas_sdk/domains/events/handlers/publish/app.py +49 -0
  61. geek_cafe_saas_sdk/domains/events/handlers/rsvp/app.py +83 -0
  62. geek_cafe_saas_sdk/domains/events/handlers/update/app.py +44 -0
  63. geek_cafe_saas_sdk/domains/events/models/__init__.py +3 -0
  64. geek_cafe_saas_sdk/domains/events/models/event.py +681 -0
  65. geek_cafe_saas_sdk/domains/events/models/event_attendee.py +324 -0
  66. geek_cafe_saas_sdk/domains/events/services/__init__.py +9 -0
  67. geek_cafe_saas_sdk/domains/events/services/event_attendee_service.py +571 -0
  68. geek_cafe_saas_sdk/domains/events/services/event_service.py +684 -0
  69. geek_cafe_saas_sdk/domains/files/__init__.py +0 -0
  70. geek_cafe_saas_sdk/domains/files/models/__init__.py +0 -0
  71. geek_cafe_saas_sdk/domains/files/models/directory.py +258 -0
  72. geek_cafe_saas_sdk/domains/files/models/file.py +312 -0
  73. geek_cafe_saas_sdk/domains/files/models/file_share.py +268 -0
  74. geek_cafe_saas_sdk/domains/files/models/file_version.py +216 -0
  75. geek_cafe_saas_sdk/domains/files/services/__init__.py +0 -0
  76. geek_cafe_saas_sdk/domains/files/services/directory_service.py +701 -0
  77. geek_cafe_saas_sdk/domains/files/services/file_share_service.py +663 -0
  78. geek_cafe_saas_sdk/domains/files/services/file_system_service.py +575 -0
  79. geek_cafe_saas_sdk/domains/files/services/file_version_service.py +739 -0
  80. geek_cafe_saas_sdk/domains/files/services/s3_file_service.py +501 -0
  81. geek_cafe_saas_sdk/domains/messaging/__init__.py +0 -0
  82. geek_cafe_saas_sdk/domains/messaging/handlers/__init__.py +0 -0
  83. geek_cafe_saas_sdk/domains/messaging/handlers/chat_channels/create/app.py +86 -0
  84. geek_cafe_saas_sdk/domains/messaging/handlers/chat_channels/delete/app.py +65 -0
  85. geek_cafe_saas_sdk/domains/messaging/handlers/chat_channels/get/app.py +64 -0
  86. geek_cafe_saas_sdk/domains/messaging/handlers/chat_channels/list/app.py +97 -0
  87. geek_cafe_saas_sdk/domains/messaging/handlers/chat_channels/update/app.py +149 -0
  88. geek_cafe_saas_sdk/domains/messaging/handlers/chat_messages/create/app.py +67 -0
  89. geek_cafe_saas_sdk/domains/messaging/handlers/chat_messages/delete/app.py +65 -0
  90. geek_cafe_saas_sdk/domains/messaging/handlers/chat_messages/get/app.py +64 -0
  91. geek_cafe_saas_sdk/domains/messaging/handlers/chat_messages/list/app.py +102 -0
  92. geek_cafe_saas_sdk/domains/messaging/handlers/chat_messages/update/app.py +127 -0
  93. geek_cafe_saas_sdk/domains/messaging/handlers/contact_threads/create/app.py +94 -0
  94. geek_cafe_saas_sdk/domains/messaging/handlers/contact_threads/delete/app.py +66 -0
  95. geek_cafe_saas_sdk/domains/messaging/handlers/contact_threads/get/app.py +67 -0
  96. geek_cafe_saas_sdk/domains/messaging/handlers/contact_threads/list/app.py +95 -0
  97. geek_cafe_saas_sdk/domains/messaging/handlers/contact_threads/update/app.py +156 -0
  98. geek_cafe_saas_sdk/domains/messaging/models/__init__.py +13 -0
  99. geek_cafe_saas_sdk/domains/messaging/models/chat_channel.py +337 -0
  100. geek_cafe_saas_sdk/domains/messaging/models/chat_channel_member.py +180 -0
  101. geek_cafe_saas_sdk/domains/messaging/models/chat_message.py +426 -0
  102. geek_cafe_saas_sdk/domains/messaging/models/contact_thread.py +392 -0
  103. geek_cafe_saas_sdk/domains/messaging/services/__init__.py +11 -0
  104. geek_cafe_saas_sdk/domains/messaging/services/chat_channel_service.py +700 -0
  105. geek_cafe_saas_sdk/domains/messaging/services/chat_message_service.py +491 -0
  106. geek_cafe_saas_sdk/domains/messaging/services/contact_thread_service.py +497 -0
  107. geek_cafe_saas_sdk/domains/tenancy/__init__.py +0 -0
  108. geek_cafe_saas_sdk/domains/tenancy/handlers/__init__.py +0 -0
  109. geek_cafe_saas_sdk/domains/tenancy/handlers/subscriptions/activate/app.py +52 -0
  110. geek_cafe_saas_sdk/domains/tenancy/handlers/subscriptions/active/app.py +37 -0
  111. geek_cafe_saas_sdk/domains/tenancy/handlers/subscriptions/cancel/app.py +55 -0
  112. geek_cafe_saas_sdk/domains/tenancy/handlers/subscriptions/get/app.py +39 -0
  113. geek_cafe_saas_sdk/domains/tenancy/handlers/subscriptions/list/app.py +44 -0
  114. geek_cafe_saas_sdk/domains/tenancy/handlers/subscriptions/record_payment/app.py +56 -0
  115. geek_cafe_saas_sdk/domains/tenancy/handlers/tenants/get/app.py +39 -0
  116. geek_cafe_saas_sdk/domains/tenancy/handlers/tenants/me/app.py +37 -0
  117. geek_cafe_saas_sdk/domains/tenancy/handlers/tenants/signup/app.py +61 -0
  118. geek_cafe_saas_sdk/domains/tenancy/handlers/tenants/update/app.py +44 -0
  119. geek_cafe_saas_sdk/domains/tenancy/models/__init__.py +6 -0
  120. geek_cafe_saas_sdk/domains/tenancy/models/subscription.py +440 -0
  121. geek_cafe_saas_sdk/domains/tenancy/models/tenant.py +258 -0
  122. geek_cafe_saas_sdk/domains/tenancy/services/__init__.py +6 -0
  123. geek_cafe_saas_sdk/domains/tenancy/services/subscription_service.py +557 -0
  124. geek_cafe_saas_sdk/domains/tenancy/services/tenant_service.py +575 -0
  125. geek_cafe_saas_sdk/domains/voting/__init__.py +0 -0
  126. geek_cafe_saas_sdk/domains/voting/handlers/__init__.py +0 -0
  127. geek_cafe_saas_sdk/domains/voting/handlers/votes/create/app.py +128 -0
  128. geek_cafe_saas_sdk/domains/voting/handlers/votes/delete/app.py +41 -0
  129. geek_cafe_saas_sdk/domains/voting/handlers/votes/get/app.py +39 -0
  130. geek_cafe_saas_sdk/domains/voting/handlers/votes/list/app.py +38 -0
  131. geek_cafe_saas_sdk/domains/voting/handlers/votes/summerize/README.md +3 -0
  132. geek_cafe_saas_sdk/domains/voting/handlers/votes/update/app.py +44 -0
  133. geek_cafe_saas_sdk/domains/voting/models/__init__.py +9 -0
  134. geek_cafe_saas_sdk/domains/voting/models/vote.py +231 -0
  135. geek_cafe_saas_sdk/domains/voting/models/vote_summary.py +193 -0
  136. geek_cafe_saas_sdk/domains/voting/services/__init__.py +11 -0
  137. geek_cafe_saas_sdk/domains/voting/services/vote_service.py +264 -0
  138. geek_cafe_saas_sdk/domains/voting/services/vote_summary_service.py +198 -0
  139. geek_cafe_saas_sdk/domains/voting/services/vote_tally_service.py +533 -0
  140. geek_cafe_saas_sdk/lambda_handlers/README.md +404 -0
  141. geek_cafe_saas_sdk/lambda_handlers/__init__.py +67 -0
  142. geek_cafe_saas_sdk/lambda_handlers/_base/__init__.py +25 -0
  143. geek_cafe_saas_sdk/lambda_handlers/_base/api_key_handler.py +129 -0
  144. geek_cafe_saas_sdk/lambda_handlers/_base/authorized_secure_handler.py +218 -0
  145. geek_cafe_saas_sdk/lambda_handlers/_base/base_handler.py +185 -0
  146. geek_cafe_saas_sdk/lambda_handlers/_base/handler_factory.py +256 -0
  147. geek_cafe_saas_sdk/lambda_handlers/_base/public_handler.py +53 -0
  148. geek_cafe_saas_sdk/lambda_handlers/_base/secure_handler.py +89 -0
  149. geek_cafe_saas_sdk/lambda_handlers/_base/service_pool.py +94 -0
  150. geek_cafe_saas_sdk/lambda_handlers/directories/create/app.py +79 -0
  151. geek_cafe_saas_sdk/lambda_handlers/directories/delete/app.py +76 -0
  152. geek_cafe_saas_sdk/lambda_handlers/directories/get/app.py +74 -0
  153. geek_cafe_saas_sdk/lambda_handlers/directories/list/app.py +75 -0
  154. geek_cafe_saas_sdk/lambda_handlers/directories/move/app.py +79 -0
  155. geek_cafe_saas_sdk/lambda_handlers/files/delete/app.py +121 -0
  156. geek_cafe_saas_sdk/lambda_handlers/files/download/app.py +187 -0
  157. geek_cafe_saas_sdk/lambda_handlers/files/get/app.py +127 -0
  158. geek_cafe_saas_sdk/lambda_handlers/files/list/app.py +108 -0
  159. geek_cafe_saas_sdk/lambda_handlers/files/share/app.py +83 -0
  160. geek_cafe_saas_sdk/lambda_handlers/files/shares/list/app.py +84 -0
  161. geek_cafe_saas_sdk/lambda_handlers/files/shares/revoke/app.py +76 -0
  162. geek_cafe_saas_sdk/lambda_handlers/files/update/app.py +143 -0
  163. geek_cafe_saas_sdk/lambda_handlers/files/upload/app.py +151 -0
  164. geek_cafe_saas_sdk/middleware/__init__.py +36 -0
  165. geek_cafe_saas_sdk/middleware/auth.py +85 -0
  166. geek_cafe_saas_sdk/middleware/authorization.py +523 -0
  167. geek_cafe_saas_sdk/middleware/cors.py +63 -0
  168. geek_cafe_saas_sdk/middleware/error_handling.py +114 -0
  169. geek_cafe_saas_sdk/middleware/validation.py +80 -0
  170. geek_cafe_saas_sdk/models/__init__.py +20 -0
  171. geek_cafe_saas_sdk/models/base_model.py +233 -0
  172. geek_cafe_saas_sdk/services/__init__.py +18 -0
  173. geek_cafe_saas_sdk/services/database_service.py +441 -0
  174. geek_cafe_saas_sdk/utilities/__init__.py +88 -0
  175. geek_cafe_saas_sdk/utilities/cognito_utility.py +568 -0
  176. geek_cafe_saas_sdk/utilities/custom_exceptions.py +183 -0
  177. geek_cafe_saas_sdk/utilities/datetime_utility.py +410 -0
  178. geek_cafe_saas_sdk/utilities/dictionary_utility.py +78 -0
  179. geek_cafe_saas_sdk/utilities/dynamodb_utils.py +151 -0
  180. geek_cafe_saas_sdk/utilities/environment_loader.py +149 -0
  181. geek_cafe_saas_sdk/utilities/environment_variables.py +228 -0
  182. geek_cafe_saas_sdk/utilities/http_body_parameters.py +44 -0
  183. geek_cafe_saas_sdk/utilities/http_path_parameters.py +60 -0
  184. geek_cafe_saas_sdk/utilities/http_status_code.py +63 -0
  185. geek_cafe_saas_sdk/utilities/jwt_utility.py +234 -0
  186. geek_cafe_saas_sdk/utilities/lambda_event_utility.py +776 -0
  187. geek_cafe_saas_sdk/utilities/logging_utility.py +64 -0
  188. geek_cafe_saas_sdk/utilities/message_query_helper.py +340 -0
  189. geek_cafe_saas_sdk/utilities/response.py +209 -0
  190. geek_cafe_saas_sdk/utilities/string_functions.py +180 -0
  191. geek_cafe_saas_sdk-0.6.0.dist-info/METADATA +397 -0
  192. geek_cafe_saas_sdk-0.6.0.dist-info/RECORD +194 -0
  193. geek_cafe_saas_sdk-0.6.0.dist-info/WHEEL +4 -0
  194. geek_cafe_saas_sdk-0.6.0.dist-info/licenses/LICENSE +47 -0
@@ -0,0 +1,404 @@
1
+ # Lambda Handler Reference Implementations
2
+
3
+ ## Overview
4
+
5
+ This directory contains **reference Lambda handler implementations** for the file system services. These handlers are **templates** meant to be copied and customized by projects consuming this SDK.
6
+
7
+ ## Important: These Are Not Deployed
8
+
9
+ **This SDK does not deploy Lambda functions to AWS.** These handlers serve as:
10
+
11
+ - ✅ **Reference implementations** showing how to use the SDK services
12
+ - ✅ **Starting templates** for your own Lambda functions
13
+ - ✅ **Best practice examples** for structuring handlers
14
+ - ✅ **Integration patterns** with API Gateway
15
+
16
+ ## How to Use
17
+
18
+ ### 1. Install the SDK in Your Project
19
+
20
+ ```bash
21
+ pip install geek-cafe-saas-sdk
22
+ ```
23
+
24
+ ### 2. Copy Handlers You Need
25
+
26
+ Copy the handler files into your project:
27
+
28
+ ```bash
29
+ # Copy specific handler
30
+ cp lambda_handlers/files/upload/app.py my-project/handlers/file_upload.py
31
+
32
+ # Or copy entire categories
33
+ cp -r lambda_handlers/files my-project/handlers/
34
+ ```
35
+
36
+ ### 3. Customize for Your Needs
37
+
38
+ All handlers follow this pattern:
39
+
40
+ ```python
41
+ from geek_cafe_saas_sdk.domains.files.services.file_system_service import FileSystemService
42
+
43
+ def lambda_handler(event, context):
44
+ # 1. Extract parameters from event
45
+ # 2. Initialize services from SDK
46
+ # 3. Call service methods
47
+ # 4. Return formatted response
48
+ ```
49
+
50
+ Customize:
51
+ - **Authentication**: Add Cognito, JWT, or custom auth
52
+ - **Validation**: Add business-specific rules
53
+ - **Error handling**: Match your error format
54
+ - **Logging**: Integrate with your logging system
55
+ - **Metrics**: Add custom CloudWatch metrics
56
+
57
+ ### 4. Deploy with Your Infrastructure
58
+
59
+ Deploy using your preferred tool:
60
+
61
+ **AWS SAM:**
62
+ ```yaml
63
+ FileUploadFunction:
64
+ Type: AWS::Serverless::Function
65
+ Properties:
66
+ CodeUri: handlers/
67
+ Handler: file_upload.lambda_handler
68
+ ```
69
+
70
+ **Serverless Framework:**
71
+ ```yaml
72
+ functions:
73
+ fileUpload:
74
+ handler: handlers/file_upload.lambda_handler
75
+ ```
76
+
77
+ **AWS CDK:**
78
+ ```python
79
+ lambda_.Function(
80
+ self, "FileUpload",
81
+ handler="file_upload.lambda_handler",
82
+ code=lambda_.Code.from_asset("handlers")
83
+ )
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Handler Structure
89
+
90
+ ### File Operations
91
+
92
+ | Handler | Path | Purpose |
93
+ |---------|------|---------|
94
+ | `files/upload/app.py` | `POST /files` | Upload new file |
95
+ | `files/download/app.py` | `GET /files/{id}/download` | Download file |
96
+ | `files/get/app.py` | `GET /files/{id}` | Get metadata |
97
+ | `files/update/app.py` | `PUT /files/{id}` | Update metadata |
98
+ | `files/delete/app.py` | `DELETE /files/{id}` | Delete file |
99
+ | `files/list/app.py` | `GET /files` | List files |
100
+
101
+ ### Directory Operations
102
+
103
+ | Handler | Path | Purpose |
104
+ |---------|------|---------|
105
+ | `directories/create/app.py` | `POST /directories` | Create directory |
106
+ | `directories/get/app.py` | `GET /directories/{id}` | Get directory |
107
+ | `directories/list/app.py` | `GET /directories` | List directories |
108
+ | `directories/move/app.py` | `PUT /directories/{id}/move` | Move directory |
109
+ | `directories/delete/app.py` | `DELETE /directories/{id}` | Delete directory |
110
+
111
+ ### Sharing Operations
112
+
113
+ | Handler | Path | Purpose |
114
+ |---------|------|---------|
115
+ | `files/share/app.py` | `POST /files/share` | Share file |
116
+ | `files/shares/list/app.py` | `GET /files/shares` | List shares |
117
+ | `files/shares/revoke/app.py` | `DELETE /files/shares/{id}` | Revoke share |
118
+
119
+ ---
120
+
121
+ ## Common Customizations
122
+
123
+ ### Add Authentication
124
+
125
+ ```python
126
+ def lambda_handler(event, context):
127
+ # Validate JWT token
128
+ token = event['headers'].get('Authorization')
129
+ user = validate_token(token)
130
+
131
+ # Use validated user_id
132
+ result = file_service.create(
133
+ tenant_id=user.tenant_id,
134
+ user_id=user.user_id,
135
+ ...
136
+ )
137
+ ```
138
+
139
+ ### Add Request Validation
140
+
141
+ ```python
142
+ def lambda_handler(event, context):
143
+ body = json.loads(event['body'])
144
+
145
+ # Validate with your schema
146
+ errors = validate_upload_request(body)
147
+ if errors:
148
+ return {
149
+ 'statusCode': 400,
150
+ 'body': json.dumps({'errors': errors})
151
+ }
152
+ ```
153
+
154
+ ### Add Custom Error Handling
155
+
156
+ ```python
157
+ def lambda_handler(event, context):
158
+ try:
159
+ result = file_service.create(...)
160
+
161
+ if not result.success:
162
+ # Map SDK errors to your error codes
163
+ error_code = map_error_code(result.error_code)
164
+ return custom_error_response(error_code, result.message)
165
+
166
+ except CustomException as e:
167
+ # Handle business exceptions
168
+ return business_error_response(e)
169
+ ```
170
+
171
+ ### Add Logging and Metrics
172
+
173
+ ```python
174
+ import logging
175
+ from aws_lambda_powertools import Logger, Metrics
176
+
177
+ logger = Logger()
178
+ metrics = Metrics()
179
+
180
+ @logger.inject_lambda_context
181
+ @metrics.log_metrics
182
+ def lambda_handler(event, context):
183
+ logger.info("File upload requested", extra={
184
+ "tenant_id": tenant_id,
185
+ "file_name": file_name
186
+ })
187
+
188
+ metrics.add_metric(name="FileUploads", unit="Count", value=1)
189
+
190
+ result = file_service.create(...)
191
+
192
+ if result.success:
193
+ metrics.add_metric(name="FileUploadSuccess", unit="Count", value=1)
194
+ else:
195
+ metrics.add_metric(name="FileUploadFailure", unit="Count", value=1)
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Environment Variables
201
+
202
+ All handlers expect these environment variables:
203
+
204
+ ```bash
205
+ DYNAMODB_TABLE_NAME=your-table-name
206
+ S3_BUCKET_NAME=your-bucket-name
207
+ AWS_REGION=us-east-1
208
+ ```
209
+
210
+ Set these in your infrastructure code:
211
+
212
+ ```yaml
213
+ # SAM
214
+ Environment:
215
+ Variables:
216
+ DYNAMODB_TABLE_NAME: !Ref FilesTable
217
+ S3_BUCKET_NAME: !Ref FilesBucket
218
+ ```
219
+
220
+ ---
221
+
222
+ ## Required Dependencies
223
+
224
+ When deploying, include:
225
+
226
+ ```
227
+ geek-cafe-saas-sdk
228
+ boto3
229
+ boto3-assist
230
+ ```
231
+
232
+ Use Lambda Layers for dependencies:
233
+
234
+ ```bash
235
+ # Create layer
236
+ mkdir -p layer/python
237
+ pip install -t layer/python geek-cafe-saas-sdk boto3-assist
238
+ cd layer && zip -r ../dependencies.zip .
239
+ ```
240
+
241
+ ---
242
+
243
+ ## Testing Handlers Locally
244
+
245
+ ### With SAM CLI
246
+
247
+ ```bash
248
+ # Create test event
249
+ cat > test-event.json <<EOF
250
+ {
251
+ "body": {
252
+ "tenant_id": "test",
253
+ "user_id": "user1",
254
+ "file_name": "test.txt",
255
+ "file_data": "SGVsbG8="
256
+ }
257
+ }
258
+ EOF
259
+
260
+ # Invoke locally
261
+ sam local invoke FileUploadFunction -e test-event.json
262
+ ```
263
+
264
+ ### With Python
265
+
266
+ ```python
267
+ from your_handlers.file_upload import lambda_handler
268
+
269
+ event = {
270
+ "body": {
271
+ "tenant_id": "test",
272
+ "user_id": "user1",
273
+ "file_name": "test.txt",
274
+ "file_data": "SGVsbG8="
275
+ }
276
+ }
277
+
278
+ response = lambda_handler(event, None)
279
+ print(response)
280
+ ```
281
+
282
+ ---
283
+
284
+ ## Example: Complete Custom Handler
285
+
286
+ Here's how to create a custom handler based on the SDK:
287
+
288
+ ```python
289
+ """
290
+ Custom file upload handler for MyApp.
291
+ """
292
+
293
+ import json
294
+ import base64
295
+ import os
296
+ from typing import Dict, Any
297
+
298
+ from geek_cafe_saas_sdk.domains.files.services.file_system_service import FileSystemService
299
+ from geek_cafe_saas_sdk.domains.files.services.s3_file_service import S3FileService
300
+ from boto3_assist.dynamodb.dynamodb import DynamoDB
301
+ from boto3_assist.s3.s3_connection import S3Connection
302
+ from boto3_assist.s3.s3_object import S3Object
303
+ from boto3_assist.s3.s3_bucket import S3Bucket
304
+
305
+ from myapp.auth import validate_jwt
306
+ from myapp.validation import validate_file_upload
307
+ from myapp.logging import get_logger
308
+
309
+ logger = get_logger(__name__)
310
+
311
+
312
+ def lambda_handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]:
313
+ """Custom file upload handler with MyApp-specific logic."""
314
+
315
+ # 1. Authenticate user
316
+ try:
317
+ user = validate_jwt(event['headers']['Authorization'])
318
+ except Exception as e:
319
+ return {
320
+ 'statusCode': 401,
321
+ 'body': json.dumps({'error': 'Unauthorized'})
322
+ }
323
+
324
+ # 2. Parse and validate request
325
+ body = json.loads(event['body'])
326
+ errors = validate_file_upload(body, user)
327
+ if errors:
328
+ return {
329
+ 'statusCode': 400,
330
+ 'body': json.dumps({'errors': errors})
331
+ }
332
+
333
+ # 3. Initialize SDK services
334
+ db = DynamoDB()
335
+ connection = S3Connection()
336
+
337
+ s3_service = S3FileService(
338
+ s3_object=S3Object(connection=connection),
339
+ s3_bucket=S3Bucket(connection=connection),
340
+ default_bucket=os.environ['S3_BUCKET_NAME']
341
+ )
342
+
343
+ file_service = FileSystemService(
344
+ dynamodb=db,
345
+ table_name=os.environ['DYNAMODB_TABLE_NAME'],
346
+ s3_service=s3_service,
347
+ default_bucket=os.environ['S3_BUCKET_NAME']
348
+ )
349
+
350
+ # 4. Upload file using SDK
351
+ try:
352
+ result = file_service.create(
353
+ tenant_id=user.tenant_id,
354
+ user_id=user.user_id,
355
+ file_name=body['file_name'],
356
+ file_data=base64.b64decode(body['file_data']),
357
+ mime_type=body.get('mime_type', 'application/octet-stream'),
358
+ directory_id=body.get('directory_id'),
359
+ tags=body.get('tags', [])
360
+ )
361
+
362
+ if result.success:
363
+ logger.info(f"File uploaded: {result.data.file_id}")
364
+
365
+ # 5. Return MyApp-formatted response
366
+ return {
367
+ 'statusCode': 201,
368
+ 'body': json.dumps({
369
+ 'id': result.data.file_id,
370
+ 'name': result.data.file_name,
371
+ 'size': result.data.file_size,
372
+ 'created_at': result.data.created_utc_ts
373
+ })
374
+ }
375
+ else:
376
+ logger.error(f"Upload failed: {result.message}")
377
+ return {
378
+ 'statusCode': 400,
379
+ 'body': json.dumps({
380
+ 'error': result.message,
381
+ 'code': result.error_code
382
+ })
383
+ }
384
+
385
+ except Exception as e:
386
+ logger.exception("Unexpected error during upload")
387
+ return {
388
+ 'statusCode': 500,
389
+ 'body': json.dumps({'error': 'Internal server error'})
390
+ }
391
+ ```
392
+
393
+ ---
394
+
395
+ ## Support
396
+
397
+ - **Service Documentation**: See main SDK docs for service usage
398
+ - **Usage Examples**: See `docs/FILE_SYSTEM_USAGE.md`
399
+ - **API Reference**: See `docs/FILE_SYSTEM_API.md`
400
+ - **Integration Tests**: See `tests/test_files_integration.py` for complete workflows
401
+
402
+ ---
403
+
404
+ **Remember**: These handlers are starting points. Customize them to fit your application's needs!
@@ -0,0 +1,67 @@
1
+ """
2
+ Lambda handler wrappers for reducing boilerplate in AWS Lambda functions.
3
+
4
+ This module provides a flexible, composable system for creating Lambda handlers
5
+ with built-in support for:
6
+ - API key validation
7
+ - API Gateway authorization (Cognito/Lambda authorizers)
8
+ - Public endpoints (no auth)
9
+ - Request/response transformation
10
+ - Service initialization and pooling
11
+ - Error handling and CORS
12
+ - User context extraction
13
+
14
+ Example Usage with Factory (Recommended):
15
+ from geek_cafe_saas_sdk.lambda_handlers import create_handler
16
+ from geek_cafe_saas_sdk.services.vote_service import VoteService
17
+
18
+ # Factory automatically selects handler based on AUTH_TYPE env var
19
+ # Defaults to secure (API Gateway authorizer) if not set
20
+ handler = create_handler(
21
+ service_class=VoteService,
22
+ require_body=True,
23
+ convert_case=True
24
+ )
25
+
26
+ def lambda_handler(event, context):
27
+ return handler.execute(event, context, business_logic)
28
+
29
+ def business_logic(event, service, user_context):
30
+ payload = event["parsed_body"]
31
+ return service.create_vote(...)
32
+
33
+ Environment Configuration:
34
+ AUTH_TYPE=secure # API Gateway authorizer (default)
35
+ AUTH_TYPE=api_key # x-api-key header validation
36
+ AUTH_TYPE=public # No authentication
37
+
38
+ AUTH_STRICT=true # Strict validation (default)
39
+ AUTH_STRICT=false # Permissive for local dev
40
+
41
+ Direct Usage (if needed):
42
+ from geek_cafe_saas_sdk.lambda_handlers import SecureLambdaHandler
43
+
44
+ handler = SecureLambdaHandler(
45
+ service_class=VoteService,
46
+ require_body=True
47
+ )
48
+ """
49
+
50
+ from ._base.base_handler import BaseLambdaHandler
51
+ from ._base.api_key_handler import ApiKeyLambdaHandler
52
+ from ._base.public_handler import PublicLambdaHandler
53
+ from ._base.secure_handler import SecureLambdaHandler
54
+ from ._base.authorized_secure_handler import AuthorizedSecureLambdaHandler
55
+ from ._base.service_pool import ServicePool
56
+ from ._base.handler_factory import HandlerFactory, create_handler
57
+
58
+ __all__ = [
59
+ "BaseLambdaHandler",
60
+ "ApiKeyLambdaHandler",
61
+ "PublicLambdaHandler",
62
+ "SecureLambdaHandler",
63
+ "AuthorizedSecureLambdaHandler",
64
+ "ServicePool",
65
+ "HandlerFactory",
66
+ "create_handler",
67
+ ]
@@ -0,0 +1,25 @@
1
+ """
2
+ Base Lambda handler implementations.
3
+
4
+ This module contains the core handler infrastructure that all Lambda handlers
5
+ can build upon. These are internal/shared components.
6
+ """
7
+
8
+ from .base_handler import BaseLambdaHandler
9
+ from .api_key_handler import ApiKeyLambdaHandler
10
+ from .public_handler import PublicLambdaHandler
11
+ from .secure_handler import SecureLambdaHandler
12
+ from .authorized_secure_handler import AuthorizedSecureLambdaHandler
13
+ from .service_pool import ServicePool
14
+ from .handler_factory import HandlerFactory, create_handler
15
+
16
+ __all__ = [
17
+ 'BaseLambdaHandler',
18
+ 'ApiKeyLambdaHandler',
19
+ 'PublicLambdaHandler',
20
+ 'SecureLambdaHandler',
21
+ 'AuthorizedSecureLambdaHandler',
22
+ 'ServicePool',
23
+ 'HandlerFactory',
24
+ 'create_handler',
25
+ ]
@@ -0,0 +1,129 @@
1
+ """
2
+ Lambda handler with API key validation.
3
+
4
+ Implements the API key validation pattern used across the application.
5
+ """
6
+
7
+ import os
8
+ from typing import Dict, Any, Optional, Callable, TypeVar
9
+ from aws_lambda_powertools import Logger
10
+
11
+ from geek_cafe_saas_sdk.utilities.response import error_response
12
+ from geek_cafe_saas_sdk.utilities.lambda_event_utility import LambdaEventUtility
13
+ from .base_handler import BaseLambdaHandler
14
+
15
+ logger = Logger()
16
+ T = TypeVar('T')
17
+
18
+
19
+ class ApiKeyLambdaHandler(BaseLambdaHandler):
20
+ """
21
+ Lambda handler with API key validation.
22
+
23
+ Validates that requests include a valid API key in the x-api-key header.
24
+ The expected API key is read from the API_KEY environment variable.
25
+
26
+ Example:
27
+ handler = ApiKeyLambdaHandler(
28
+ service_class=VoteService,
29
+ require_body=True
30
+ )
31
+
32
+ def lambda_handler(event, context):
33
+ return handler.execute(event, context, process_vote)
34
+
35
+ def process_vote(event, service, user_context):
36
+ payload = event["parsed_body"]
37
+ return service.create_vote(...)
38
+ """
39
+
40
+ def __init__(
41
+ self,
42
+ api_key_env_var: str = "API_KEY",
43
+ api_key_header: str = "x-api-key",
44
+ **kwargs
45
+ ):
46
+ """
47
+ Initialize the API key handler.
48
+
49
+ Args:
50
+ api_key_env_var: Environment variable name for API key
51
+ api_key_header: Header name to check for API key
52
+ **kwargs: Arguments passed to BaseLambdaHandler
53
+ """
54
+ # API key handlers don't require JWT auth - API key IS the auth
55
+ kwargs.setdefault('require_auth', False)
56
+ super().__init__(**kwargs)
57
+ self.api_key_env_var = api_key_env_var
58
+ self.api_key_header = api_key_header
59
+
60
+ def _validate_security(self, event: Dict[str, Any]) -> Optional[Dict[str, Any]]:
61
+ """
62
+ Validate API key is present and correct at request time.
63
+
64
+ This method reads the API_KEY environment variable within the request
65
+ cycle, making it testable.
66
+
67
+ Returns:
68
+ Error response if validation fails, None if valid.
69
+ """
70
+ # 1. Get expected key from environment (at request time)
71
+ expected_api_key = os.getenv(self.api_key_env_var)
72
+
73
+ # 2. Check if the key is configured in the environment
74
+ if not expected_api_key:
75
+ logger.error(
76
+ f"API key not configured. Set '{self.api_key_env_var}' environment variable."
77
+ )
78
+ # This is a server-side configuration error
79
+ return error_response(
80
+ "This endpoint is not configured for API key access.",
81
+ "CONFIGURATION_ERROR",
82
+ 500,
83
+ )
84
+
85
+ # 3. Get provided key from request headers
86
+ provided_api_key = LambdaEventUtility.get_value_from_header(
87
+ event, self.api_key_header
88
+ )
89
+
90
+ # 4. Check if a key was provided
91
+ if not provided_api_key:
92
+ logger.warning(f"No API key provided in '{self.api_key_header}' header.")
93
+ return error_response("API key is required.", "UNAUTHORIZED", 401)
94
+
95
+ # 5. Validate the key
96
+ if provided_api_key != expected_api_key:
97
+ logger.warning("Invalid API key provided.")
98
+ return error_response("Invalid API key.", "UNAUTHORIZED", 401)
99
+
100
+ # 6. Validation passed
101
+ logger.info("API key validation successful.")
102
+ return None
103
+
104
+ def execute(
105
+ self,
106
+ event: Dict[str, Any],
107
+ context: Any,
108
+ business_logic: Callable[[Dict[str, Any], Any, Dict[str, Any]], Any],
109
+ injected_service: Optional[T] = None
110
+ ) -> Dict[str, Any]:
111
+ """
112
+ Execute the Lambda handler with API key validation.
113
+
114
+ Args:
115
+ event: Lambda event dictionary
116
+ context: Lambda context object
117
+ business_logic: Callable that implements the business logic
118
+ injected_service: Optional service instance for testing
119
+
120
+ Returns:
121
+ Lambda response dictionary
122
+ """
123
+ # Validate API key first
124
+ validation_error = self._validate_security(event)
125
+ if validation_error:
126
+ return validation_error
127
+
128
+ # If validation passed, execute parent's execute method
129
+ return super().execute(event, context, business_logic, injected_service)