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,93 @@
1
+ # src/geek_cafe_saas_sdk/lambda_handlers/events/create/app.py
2
+
3
+ import json
4
+ from typing import Dict, Any
5
+
6
+ from geek_cafe_saas_sdk.domains.events.services.event_service import EventService
7
+ from geek_cafe_saas_sdk.lambda_handlers import ServicePool
8
+ from geek_cafe_saas_sdk.utilities.response import service_result_to_response, error_response
9
+ from geek_cafe_saas_sdk.utilities.lambda_event_utility import LambdaEventUtility
10
+ from geek_cafe_saas_sdk.domains.events.models.event import Event
11
+
12
+ event_service_pool = ServicePool(EventService)
13
+
14
+ def handler(event: Dict[str, Any], context: object, injected_service=None) -> Dict[str, Any]:
15
+ """
16
+ Lambda handler for creating a new event.
17
+
18
+ Supports both timestamp-based and ISO8601 datetime formats.
19
+ Automatically creates EventAttendee record for owner as organizer.
20
+
21
+ Args:
22
+ event: API Gateway event
23
+ context: Lambda context
24
+ injected_service: Optional EventService for testing (Moto)
25
+
26
+ Expected body (all optional except title and start time):
27
+ {
28
+ "title": "Python Meetup",
29
+ "start_utc_ts": 1731717600.0, // OR use start_datetime
30
+ "start_datetime": "2025-11-15T18:00:00-08:00", // Auto-converts to timestamp
31
+ "end_utc_ts": 1731728400.0, // OR use end_datetime
32
+ "end_datetime": "2025-11-15T21:00:00-08:00",
33
+ "timezone": "America/Los_Angeles",
34
+ "description": "Monthly Python meetup",
35
+ "event_type": "meetup",
36
+ "status": "draft", // or "published"
37
+ "visibility": "public",
38
+
39
+ // Location
40
+ "location_type": "physical", // physical, virtual, hybrid
41
+ "location_name": "Tech Hub SF",
42
+ "location_address": "123 Main St",
43
+ "location_city": "San Francisco",
44
+ "location_state": "CA",
45
+ "location_country": "US",
46
+ "location_latitude": 37.7749,
47
+ "location_longitude": -122.4194,
48
+ "virtual_link": "https://zoom.us/...",
49
+
50
+ // Capacity
51
+ "max_attendees": 50,
52
+ "allow_waitlist": true,
53
+ "requires_approval": false,
54
+ "allow_guest_plus_one": true,
55
+ "registration_deadline_utc_ts": 1731600000.0,
56
+
57
+ // Other
58
+ "group_id": "group_123",
59
+ "tags": ["python", "networking"],
60
+ "custom_fields": {"dietary": "text", "experience": "select"}
61
+ }
62
+ """
63
+ try:
64
+ # Use injected service (testing) or pool (production)
65
+ event_service = injected_service if injected_service else event_service_pool.get()
66
+ body = LambdaEventUtility.get_body_from_event(event)
67
+ user_id = LambdaEventUtility.get_authenticated_user_id(event)
68
+ tenant_id = LambdaEventUtility.get_authenticated_user_tenant_id(event)
69
+
70
+ # Convert ISO8601 datetime strings to timestamps if provided
71
+ if 'start_datetime' in body and 'start_utc_ts' not in body:
72
+ body['start_utc_ts'] = Event.datetime_to_utc_ts(body['start_datetime'])
73
+ if 'end_datetime' in body and 'end_utc_ts' not in body:
74
+ body['end_utc_ts'] = Event.datetime_to_utc_ts(body['end_datetime'])
75
+ if 'registration_deadline_datetime' in body and 'registration_deadline_utc_ts' not in body:
76
+ body['registration_deadline_utc_ts'] = Event.datetime_to_utc_ts(body['registration_deadline_datetime'])
77
+
78
+ # Pass all body parameters to the service
79
+ # Service will auto-create EventAttendee for owner
80
+ result = event_service.create(
81
+ tenant_id=tenant_id,
82
+ user_id=user_id,
83
+ create_organizer_attendee=True, # Auto-create attendee record
84
+ **body
85
+ )
86
+
87
+ return service_result_to_response(result, success_status=201)
88
+
89
+ except json.JSONDecodeError:
90
+ return error_response("Invalid JSON format in request body.", "VALIDATION_ERROR", 400)
91
+ except Exception as e:
92
+ # In a production environment, log the exception e
93
+ return error_response(f"An unexpected error occurred: {str(e)}", "INTERNAL_ERROR", 500)
@@ -0,0 +1,42 @@
1
+ # src/geek_cafe_saas_sdk/lambda_handlers/events/delete/app.py
2
+
3
+ from typing import Dict, Any
4
+
5
+ from geek_cafe_saas_sdk.domains.events.services.event_service import EventService
6
+ from geek_cafe_saas_sdk.lambda_handlers import ServicePool
7
+ from geek_cafe_saas_sdk.utilities.response import service_result_to_response, error_response, success_response
8
+ from geek_cafe_saas_sdk.utilities.lambda_event_utility import LambdaEventUtility
9
+
10
+ event_service_pool = ServicePool(EventService)
11
+
12
+ def handler(event: Dict[str, Any], context: object, injected_service=None) -> Dict[str, Any]:
13
+ """
14
+ Lambda handler for deleting an event by its ID.
15
+
16
+ Args:
17
+ event: API Gateway event
18
+ context: Lambda context
19
+ injected_service: Optional EventService for testing
20
+ """
21
+ try:
22
+ event_service = injected_service if injected_service else event_service_pool.get()
23
+ user_id = LambdaEventUtility.get_authenticated_user_id(event)
24
+ tenant_id = LambdaEventUtility.get_authenticated_user_tenant_id(event)
25
+ resource_id = LambdaEventUtility.get_value_from_path_parameters(event, 'id')
26
+
27
+ if not resource_id:
28
+ return error_response("Event ID is required in the path.", "VALIDATION_ERROR", 400)
29
+
30
+ result = event_service.delete(
31
+ resource_id=resource_id,
32
+ tenant_id=tenant_id,
33
+ user_id=user_id
34
+ )
35
+
36
+ if result.success:
37
+ return success_response(None, status_code=204)
38
+ else:
39
+ return service_result_to_response(result)
40
+
41
+ except Exception as e:
42
+ return error_response(f"An unexpected error occurred: {str(e)}", "INTERNAL_ERROR", 500)
@@ -0,0 +1,39 @@
1
+ # src/geek_cafe_saas_sdk/lambda_handlers/events/get/app.py
2
+
3
+ from typing import Dict, Any
4
+
5
+ from geek_cafe_saas_sdk.domains.events.services.event_service import EventService
6
+ from geek_cafe_saas_sdk.lambda_handlers import ServicePool
7
+ from geek_cafe_saas_sdk.utilities.response import service_result_to_response, error_response
8
+ from geek_cafe_saas_sdk.utilities.lambda_event_utility import LambdaEventUtility
9
+
10
+ event_service_pool = ServicePool(EventService)
11
+
12
+ def handler(event: Dict[str, Any], context: object, injected_service=None) -> Dict[str, Any]:
13
+ """
14
+ Lambda handler for retrieving a single event by its ID.
15
+
16
+ Args:
17
+ event: API Gateway event
18
+ context: Lambda context
19
+ injected_service: Optional EventService for testing
20
+ """
21
+ try:
22
+ event_service = injected_service if injected_service else event_service_pool.get()
23
+ user_id = LambdaEventUtility.get_authenticated_user_id(event)
24
+ tenant_id = LambdaEventUtility.get_authenticated_user_tenant_id(event)
25
+ resource_id = LambdaEventUtility.get_value_from_path_parameters(event, 'id')
26
+
27
+ if not resource_id:
28
+ return error_response("Event ID is required in the path.", "VALIDATION_ERROR", 400)
29
+
30
+ result = event_service.get_by_id(
31
+ resource_id=resource_id,
32
+ tenant_id=tenant_id,
33
+ user_id=user_id
34
+ )
35
+
36
+ return service_result_to_response(result)
37
+
38
+ except Exception as e:
39
+ return error_response(f"An unexpected error occurred: {str(e)}", "INTERNAL_ERROR", 500)
@@ -0,0 +1,98 @@
1
+ # src/geek_cafe_saas_sdk/lambda_handlers/events/invite/app.py
2
+
3
+ import json
4
+ from typing import Dict, Any
5
+
6
+ from geek_cafe_saas_sdk.domains.events.services.event_attendee_service import EventAttendeeService
7
+ from geek_cafe_saas_sdk.lambda_handlers import ServicePool
8
+ from geek_cafe_saas_sdk.utilities.response import service_result_to_response, error_response
9
+ from geek_cafe_saas_sdk.utilities.lambda_event_utility import LambdaEventUtility
10
+
11
+ attendee_service_pool = ServicePool(EventAttendeeService)
12
+
13
+ def handler(event: Dict[str, Any], context: object, injected_service=None) -> Dict[str, Any]:
14
+ """
15
+ Lambda handler for inviting user(s) to an event.
16
+
17
+ Supports single and bulk invitations.
18
+
19
+ Args:
20
+ event: API Gateway event
21
+ context: Lambda context
22
+ injected_service: Optional EventAttendeeService for testing
23
+
24
+ Expected body:
25
+ {
26
+ "event_id": "evt_123",
27
+ "user_id": "user_456", // For single invite
28
+ "user_ids": ["user_1", "user_2"], // For bulk invite
29
+ "role": "attendee", // or "co_host", "speaker", "volunteer"
30
+ "registration_data": {"dietary": "vegetarian"},
31
+ "registration_notes": "Looking forward to it!"
32
+ }
33
+
34
+ Returns 201 with invitation record(s)
35
+ """
36
+ try:
37
+ attendee_service = injected_service if injected_service else attendee_service_pool.get()
38
+ body = LambdaEventUtility.get_body_from_event(event)
39
+ user_id = LambdaEventUtility.get_authenticated_user_id(event)
40
+ tenant_id = LambdaEventUtility.get_authenticated_user_tenant_id(event)
41
+
42
+ # Validate required fields
43
+ event_id = body.get('event_id')
44
+ if not event_id:
45
+ return error_response("event_id is required", "VALIDATION_ERROR", 400)
46
+
47
+ # Check for bulk invite
48
+ if 'user_ids' in body:
49
+ # Bulk invite
50
+ user_ids = body.get('user_ids', [])
51
+ if not user_ids:
52
+ return error_response("user_ids must be a non-empty list", "VALIDATION_ERROR", 400)
53
+
54
+ # Extract optional params
55
+ kwargs = {}
56
+ if 'role' in body:
57
+ kwargs['role'] = body['role']
58
+ if 'registration_data' in body:
59
+ kwargs['registration_data'] = body['registration_data']
60
+ if 'registration_notes' in body:
61
+ kwargs['registration_notes'] = body['registration_notes']
62
+
63
+ result = attendee_service.bulk_invite(
64
+ event_id=event_id,
65
+ user_ids=user_ids,
66
+ tenant_id=tenant_id,
67
+ invited_by_user_id=user_id,
68
+ **kwargs
69
+ )
70
+ else:
71
+ # Single invite
72
+ invitee_user_id = body.get('user_id')
73
+ if not invitee_user_id:
74
+ return error_response("user_id or user_ids is required", "VALIDATION_ERROR", 400)
75
+
76
+ # Extract optional params
77
+ kwargs = {}
78
+ if 'role' in body:
79
+ kwargs['role'] = body['role']
80
+ if 'registration_data' in body:
81
+ kwargs['registration_data'] = body['registration_data']
82
+ if 'registration_notes' in body:
83
+ kwargs['registration_notes'] = body['registration_notes']
84
+
85
+ result = attendee_service.invite(
86
+ event_id=event_id,
87
+ user_id=invitee_user_id,
88
+ tenant_id=tenant_id,
89
+ invited_by_user_id=user_id,
90
+ **kwargs
91
+ )
92
+
93
+ return service_result_to_response(result, success_status=201)
94
+
95
+ except json.JSONDecodeError:
96
+ return error_response("Invalid JSON format in request body.", "VALIDATION_ERROR", 400)
97
+ except Exception as e:
98
+ return error_response(f"An unexpected error occurred: {str(e)}", "INTERNAL_ERROR", 500)
@@ -0,0 +1,125 @@
1
+ # src/geek_cafe_saas_sdk/lambda_handlers/events/list/app.py
2
+
3
+ from typing import Dict, Any
4
+
5
+ from geek_cafe_saas_sdk.domains.events.services.event_service import EventService
6
+ from geek_cafe_saas_sdk.lambda_handlers import ServicePool
7
+ from geek_cafe_saas_sdk.utilities.response import service_result_to_response, error_response
8
+ from geek_cafe_saas_sdk.utilities.lambda_event_utility import LambdaEventUtility
9
+
10
+ event_service_pool = ServicePool(EventService)
11
+
12
+ def handler(event: Dict[str, Any], context: object, injected_service=None) -> Dict[str, Any]:
13
+ """
14
+ Lambda handler for listing events with location-based and other filters.
15
+
16
+ Supports multiple query patterns:
17
+ - By city: ?city=San Francisco&state=CA&country=US
18
+ - By state: ?state=CA&country=US
19
+ - Nearby: ?latitude=37.7749&longitude=-122.4194&radius=25
20
+ - By owner: ?owner_id=user_123
21
+ - By type: ?event_type=meetup&status=published
22
+ - By group: ?group_id=group_123
23
+ - Public discovery: ?visibility=public&status=published
24
+ - All for tenant: (no params)
25
+
26
+ Args:
27
+ event: API Gateway event
28
+ context: Lambda context
29
+ injected_service: Optional EventService for testing
30
+
31
+ Query Parameters:
32
+ city: City name
33
+ state: State/Province/Region
34
+ country: Country code
35
+ latitude: Latitude for nearby search
36
+ longitude: Longitude for nearby search
37
+ radius: Radius in km (default 25)
38
+ owner_id: Owner user ID
39
+ event_type: Event type (meetup, conference, etc.)
40
+ group_id: Group ID
41
+ visibility: public, private, members_only
42
+ status: draft, published, cancelled, completed
43
+ limit: Max results (default 50)
44
+ """
45
+ try:
46
+ event_service = injected_service if injected_service else event_service_pool.get()
47
+ user_id = LambdaEventUtility.get_authenticated_user_id(event)
48
+ tenant_id = LambdaEventUtility.get_authenticated_user_tenant_id(event)
49
+ query_params = event.get('queryStringParameters', {}) or {}
50
+
51
+ # Extract common parameters
52
+ limit = int(query_params.get('limit', 50))
53
+
54
+ # Route to appropriate service method based on query params
55
+ if 'latitude' in query_params and 'longitude' in query_params:
56
+ # Nearby search (geo-location)
57
+ result = event_service.list_nearby(
58
+ latitude=float(query_params['latitude']),
59
+ longitude=float(query_params['longitude']),
60
+ radius_km=float(query_params.get('radius', 25)),
61
+ tenant_id=tenant_id,
62
+ limit=limit
63
+ )
64
+ elif 'city' in query_params:
65
+ # City search
66
+ result = event_service.list_by_city(
67
+ city=query_params['city'],
68
+ state=query_params.get('state'),
69
+ country=query_params.get('country'),
70
+ tenant_id=tenant_id,
71
+ limit=limit
72
+ )
73
+ elif 'state' in query_params:
74
+ # State/region search
75
+ result = event_service.list_by_state(
76
+ state=query_params['state'],
77
+ country=query_params.get('country'),
78
+ tenant_id=tenant_id,
79
+ limit=limit
80
+ )
81
+ elif 'owner_id' in query_params:
82
+ # Events by owner
83
+ result = event_service.list_by_owner(
84
+ owner_id=query_params['owner_id'],
85
+ tenant_id=tenant_id,
86
+ limit=limit
87
+ )
88
+ elif 'event_type' in query_params:
89
+ # Events by type
90
+ status = query_params.get('status', 'published')
91
+ result = event_service.list_by_type(
92
+ event_type=query_params['event_type'],
93
+ tenant_id=tenant_id,
94
+ status=status,
95
+ limit=limit
96
+ )
97
+ elif 'group_id' in query_params:
98
+ # Events by group
99
+ result = event_service.list_by_group(
100
+ group_id=query_params['group_id'],
101
+ tenant_id=tenant_id,
102
+ limit=limit
103
+ )
104
+ elif 'visibility' in query_params:
105
+ # Public discovery
106
+ status = query_params.get('status', 'published')
107
+ result = event_service.list_public_events(
108
+ tenant_id=tenant_id,
109
+ visibility=query_params['visibility'],
110
+ status=status,
111
+ limit=limit
112
+ )
113
+ else:
114
+ # All events for tenant
115
+ result = event_service.list_by_tenant(
116
+ tenant_id=tenant_id,
117
+ limit=limit
118
+ )
119
+
120
+ return service_result_to_response(result)
121
+
122
+ except ValueError as e:
123
+ return error_response(f"Invalid parameter value: {str(e)}", "VALIDATION_ERROR", 400)
124
+ except Exception as e:
125
+ return error_response(f"An unexpected error occurred: {str(e)}", "INTERNAL_ERROR", 500)
@@ -0,0 +1,49 @@
1
+ # src/geek_cafe_saas_sdk/lambda_handlers/events/publish/app.py
2
+
3
+ from typing import Dict, Any
4
+
5
+ from geek_cafe_saas_sdk.domains.events.services.event_service import EventService
6
+ from geek_cafe_saas_sdk.lambda_handlers import ServicePool
7
+ from geek_cafe_saas_sdk.utilities.response import service_result_to_response, error_response
8
+ from geek_cafe_saas_sdk.utilities.lambda_event_utility import LambdaEventUtility
9
+
10
+ event_service_pool = ServicePool(EventService)
11
+
12
+ def handler(event: Dict[str, Any], context: object, injected_service=None) -> Dict[str, Any]:
13
+ """
14
+ Lambda handler for publishing a draft event.
15
+
16
+ Changes status from 'draft' to 'published'.
17
+
18
+ Args:
19
+ event: API Gateway event
20
+ context: Lambda context
21
+ injected_service: Optional EventService for testing
22
+
23
+ Path Parameters:
24
+ id: Event ID
25
+
26
+ Example:
27
+ POST /events/{id}/publish
28
+
29
+ Returns 200 with published event
30
+ """
31
+ try:
32
+ event_service = injected_service if injected_service else event_service_pool.get()
33
+ user_id = LambdaEventUtility.get_authenticated_user_id(event)
34
+ tenant_id = LambdaEventUtility.get_authenticated_user_tenant_id(event)
35
+ resource_id = LambdaEventUtility.get_value_from_path_parameters(event, 'id')
36
+
37
+ if not resource_id:
38
+ return error_response("Event ID is required in the path.", "VALIDATION_ERROR", 400)
39
+
40
+ result = event_service.publish(
41
+ resource_id=resource_id,
42
+ tenant_id=tenant_id,
43
+ user_id=user_id
44
+ )
45
+
46
+ return service_result_to_response(result)
47
+
48
+ except Exception as e:
49
+ return error_response(f"An unexpected error occurred: {str(e)}", "INTERNAL_ERROR", 500)
@@ -0,0 +1,83 @@
1
+ # src/geek_cafe_saas_sdk/lambda_handlers/events/rsvp/app.py
2
+
3
+ import json
4
+ from typing import Dict, Any
5
+
6
+ from geek_cafe_saas_sdk.domains.events.services.event_attendee_service import EventAttendeeService
7
+ from geek_cafe_saas_sdk.lambda_handlers import ServicePool
8
+ from geek_cafe_saas_sdk.utilities.response import service_result_to_response, error_response
9
+ from geek_cafe_saas_sdk.utilities.lambda_event_utility import LambdaEventUtility
10
+
11
+ attendee_service_pool = ServicePool(EventAttendeeService)
12
+
13
+ def handler(event: Dict[str, Any], context: object, injected_service=None) -> Dict[str, Any]:
14
+ """
15
+ Lambda handler for updating RSVP status.
16
+
17
+ Allows users to accept, decline, or mark as tentative.
18
+
19
+ Args:
20
+ event: API Gateway event
21
+ context: Lambda context
22
+ injected_service: Optional EventAttendeeService for testing
23
+
24
+ Expected body:
25
+ {
26
+ "event_id": "evt_123",
27
+ "rsvp_status": "accepted", // accepted, declined, tentative
28
+ "plus_one_count": 1, // Optional
29
+ "plus_one_names": ["Jane Doe"], // Optional
30
+ "registration_data": {"dietary": "vegetarian", "experience": "beginner"}, // Optional
31
+ "registration_notes": "Excited to attend!" // Optional
32
+ }
33
+
34
+ Returns 200 with updated attendee record
35
+ """
36
+ try:
37
+ attendee_service = injected_service if injected_service else attendee_service_pool.get()
38
+ body = LambdaEventUtility.get_body_from_event(event)
39
+ user_id = LambdaEventUtility.get_authenticated_user_id(event)
40
+ tenant_id = LambdaEventUtility.get_authenticated_user_tenant_id(event)
41
+
42
+ # Validate required fields
43
+ event_id = body.get('event_id')
44
+ rsvp_status = body.get('rsvp_status')
45
+
46
+ if not event_id:
47
+ return error_response("event_id is required", "VALIDATION_ERROR", 400)
48
+ if not rsvp_status:
49
+ return error_response("rsvp_status is required", "VALIDATION_ERROR", 400)
50
+ if rsvp_status not in ['accepted', 'declined', 'tentative']:
51
+ return error_response(
52
+ "rsvp_status must be one of: accepted, declined, tentative",
53
+ "VALIDATION_ERROR",
54
+ 400
55
+ )
56
+
57
+ # Extract optional params
58
+ kwargs = {}
59
+ if 'plus_one_count' in body:
60
+ kwargs['plus_one_count'] = int(body['plus_one_count'])
61
+ if 'plus_one_names' in body:
62
+ kwargs['plus_one_names'] = body['plus_one_names']
63
+ if 'registration_data' in body:
64
+ kwargs['registration_data'] = body['registration_data']
65
+ if 'registration_notes' in body:
66
+ kwargs['registration_notes'] = body['registration_notes']
67
+
68
+ result = attendee_service.update_rsvp(
69
+ event_id=event_id,
70
+ user_id=user_id,
71
+ tenant_id=tenant_id,
72
+ rsvp_status=rsvp_status,
73
+ **kwargs
74
+ )
75
+
76
+ return service_result_to_response(result)
77
+
78
+ except json.JSONDecodeError:
79
+ return error_response("Invalid JSON format in request body.", "VALIDATION_ERROR", 400)
80
+ except ValueError as e:
81
+ return error_response(f"Invalid parameter value: {str(e)}", "VALIDATION_ERROR", 400)
82
+ except Exception as e:
83
+ return error_response(f"An unexpected error occurred: {str(e)}", "INTERNAL_ERROR", 500)
@@ -0,0 +1,44 @@
1
+ # src/geek_cafe_saas_sdk/lambda_handlers/events/update/app.py
2
+
3
+ import json
4
+ from typing import Dict, Any
5
+
6
+ from geek_cafe_saas_sdk.domains.events.services.event_service import EventService
7
+ from geek_cafe_saas_sdk.lambda_handlers import ServicePool
8
+ from geek_cafe_saas_sdk.utilities.response import service_result_to_response, error_response
9
+ from geek_cafe_saas_sdk.utilities.lambda_event_utility import LambdaEventUtility
10
+
11
+ event_service_pool = ServicePool(EventService)
12
+
13
+ def handler(event: Dict[str, Any], context: object, injected_service=None) -> Dict[str, Any]:
14
+ """
15
+ Lambda handler for updating an existing event.
16
+
17
+ Args:
18
+ event: API Gateway event
19
+ context: Lambda context
20
+ injected_service: Optional EventService for testing
21
+ """
22
+ try:
23
+ event_service = injected_service if injected_service else event_service_pool.get()
24
+ body = LambdaEventUtility.get_body_from_event(event)
25
+ user_id = LambdaEventUtility.get_authenticated_user_id(event)
26
+ tenant_id = LambdaEventUtility.get_authenticated_user_tenant_id(event)
27
+ resource_id = LambdaEventUtility.get_value_from_path_parameters(event, 'id')
28
+
29
+ if not resource_id:
30
+ return error_response("Event ID is required in the path.", "VALIDATION_ERROR", 400)
31
+
32
+ result = event_service.update(
33
+ resource_id=resource_id,
34
+ tenant_id=tenant_id,
35
+ user_id=user_id,
36
+ updates=body
37
+ )
38
+
39
+ return service_result_to_response(result)
40
+
41
+ except json.JSONDecodeError:
42
+ return error_response("Invalid JSON format in request body.", "VALIDATION_ERROR", 400)
43
+ except Exception as e:
44
+ return error_response(f"An unexpected error occurred: {str(e)}", "INTERNAL_ERROR", 500)
@@ -0,0 +1,3 @@
1
+ from .event import Event
2
+ from .event_attendee import EventAttendee
3
+ __all__ = ["Event", "EventAttendee"]