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.

Files changed (94) 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/directory.py +42 -6
  15. geek_cafe_saas_sdk/domains/files/models/file.py +158 -16
  16. geek_cafe_saas_sdk/domains/files/models/file_share.py +33 -0
  17. geek_cafe_saas_sdk/domains/files/models/file_version.py +24 -0
  18. geek_cafe_saas_sdk/domains/files/services/__init__.py +21 -0
  19. geek_cafe_saas_sdk/domains/files/services/directory_service.py +54 -135
  20. geek_cafe_saas_sdk/domains/files/services/file_lineage_service.py +487 -0
  21. geek_cafe_saas_sdk/domains/files/services/file_share_service.py +37 -120
  22. geek_cafe_saas_sdk/domains/files/services/file_system_service.py +67 -103
  23. geek_cafe_saas_sdk/domains/files/services/file_version_service.py +44 -124
  24. geek_cafe_saas_sdk/domains/messaging/services/contact_thread_service.py +55 -7
  25. geek_cafe_saas_sdk/domains/notifications/__init__.py +18 -0
  26. geek_cafe_saas_sdk/domains/notifications/handlers/__init__.py +1 -0
  27. geek_cafe_saas_sdk/domains/notifications/handlers/create_webhook/app.py +73 -0
  28. geek_cafe_saas_sdk/domains/notifications/handlers/get/app.py +40 -0
  29. geek_cafe_saas_sdk/domains/notifications/handlers/get_preferences/app.py +34 -0
  30. geek_cafe_saas_sdk/domains/notifications/handlers/list/app.py +43 -0
  31. geek_cafe_saas_sdk/domains/notifications/handlers/list_webhooks/app.py +40 -0
  32. geek_cafe_saas_sdk/domains/notifications/handlers/mark_read/app.py +40 -0
  33. geek_cafe_saas_sdk/domains/notifications/handlers/send/app.py +83 -0
  34. geek_cafe_saas_sdk/domains/notifications/handlers/update_preferences/app.py +45 -0
  35. geek_cafe_saas_sdk/domains/notifications/models/__init__.py +16 -0
  36. geek_cafe_saas_sdk/domains/notifications/models/notification.py +717 -0
  37. geek_cafe_saas_sdk/domains/notifications/models/notification_preference.py +365 -0
  38. geek_cafe_saas_sdk/domains/notifications/models/webhook_subscription.py +339 -0
  39. geek_cafe_saas_sdk/domains/notifications/services/__init__.py +10 -0
  40. geek_cafe_saas_sdk/domains/notifications/services/notification_service.py +576 -0
  41. geek_cafe_saas_sdk/domains/payments/__init__.py +16 -0
  42. geek_cafe_saas_sdk/domains/payments/handlers/README.md +334 -0
  43. geek_cafe_saas_sdk/domains/payments/handlers/__init__.py +6 -0
  44. geek_cafe_saas_sdk/domains/payments/handlers/billing_accounts/create/app.py +105 -0
  45. geek_cafe_saas_sdk/domains/payments/handlers/billing_accounts/get/app.py +60 -0
  46. geek_cafe_saas_sdk/domains/payments/handlers/billing_accounts/update/app.py +97 -0
  47. geek_cafe_saas_sdk/domains/payments/handlers/payment_intents/create/app.py +97 -0
  48. geek_cafe_saas_sdk/domains/payments/handlers/payment_intents/get/app.py +60 -0
  49. geek_cafe_saas_sdk/domains/payments/handlers/payments/get/app.py +60 -0
  50. geek_cafe_saas_sdk/domains/payments/handlers/payments/list/app.py +68 -0
  51. geek_cafe_saas_sdk/domains/payments/handlers/payments/record/app.py +118 -0
  52. geek_cafe_saas_sdk/domains/payments/handlers/refunds/create/app.py +89 -0
  53. geek_cafe_saas_sdk/domains/payments/handlers/refunds/get/app.py +60 -0
  54. geek_cafe_saas_sdk/domains/payments/models/__init__.py +17 -0
  55. geek_cafe_saas_sdk/domains/payments/models/billing_account.py +521 -0
  56. geek_cafe_saas_sdk/domains/payments/models/payment.py +639 -0
  57. geek_cafe_saas_sdk/domains/payments/models/payment_intent_ref.py +539 -0
  58. geek_cafe_saas_sdk/domains/payments/models/refund.py +404 -0
  59. geek_cafe_saas_sdk/domains/payments/services/__init__.py +11 -0
  60. geek_cafe_saas_sdk/domains/payments/services/payment_service.py +405 -0
  61. geek_cafe_saas_sdk/domains/subscriptions/__init__.py +19 -0
  62. geek_cafe_saas_sdk/domains/subscriptions/handlers/README.md +408 -0
  63. geek_cafe_saas_sdk/domains/subscriptions/handlers/__init__.py +1 -0
  64. geek_cafe_saas_sdk/domains/subscriptions/handlers/addons/create/app.py +81 -0
  65. geek_cafe_saas_sdk/domains/subscriptions/handlers/addons/get/app.py +48 -0
  66. geek_cafe_saas_sdk/domains/subscriptions/handlers/addons/list/app.py +54 -0
  67. geek_cafe_saas_sdk/domains/subscriptions/handlers/addons/update/app.py +54 -0
  68. geek_cafe_saas_sdk/domains/subscriptions/handlers/discounts/create/app.py +83 -0
  69. geek_cafe_saas_sdk/domains/subscriptions/handlers/discounts/get/app.py +47 -0
  70. geek_cafe_saas_sdk/domains/subscriptions/handlers/discounts/validate/app.py +62 -0
  71. geek_cafe_saas_sdk/domains/subscriptions/handlers/plans/create/app.py +82 -0
  72. geek_cafe_saas_sdk/domains/subscriptions/handlers/plans/get/app.py +48 -0
  73. geek_cafe_saas_sdk/domains/subscriptions/handlers/plans/list/app.py +66 -0
  74. geek_cafe_saas_sdk/domains/subscriptions/handlers/plans/update/app.py +54 -0
  75. geek_cafe_saas_sdk/domains/subscriptions/handlers/usage/aggregate/app.py +72 -0
  76. geek_cafe_saas_sdk/domains/subscriptions/handlers/usage/record/app.py +89 -0
  77. geek_cafe_saas_sdk/domains/subscriptions/models/__init__.py +13 -0
  78. geek_cafe_saas_sdk/domains/subscriptions/models/addon.py +604 -0
  79. geek_cafe_saas_sdk/domains/subscriptions/models/discount.py +492 -0
  80. geek_cafe_saas_sdk/domains/subscriptions/models/plan.py +569 -0
  81. geek_cafe_saas_sdk/domains/subscriptions/models/usage_record.py +300 -0
  82. geek_cafe_saas_sdk/domains/subscriptions/services/__init__.py +10 -0
  83. geek_cafe_saas_sdk/domains/subscriptions/services/subscription_manager_service.py +694 -0
  84. geek_cafe_saas_sdk/domains/tenancy/models/subscription.py +123 -1
  85. geek_cafe_saas_sdk/domains/tenancy/services/subscription_service.py +213 -0
  86. geek_cafe_saas_sdk/lambda_handlers/_base/base_handler.py +7 -0
  87. geek_cafe_saas_sdk/services/database_service.py +10 -6
  88. geek_cafe_saas_sdk/utilities/cognito_utility.py +16 -26
  89. geek_cafe_saas_sdk/utilities/environment_variables.py +16 -0
  90. geek_cafe_saas_sdk/utilities/logging_utility.py +77 -0
  91. {geek_cafe_saas_sdk-0.6.0.dist-info → geek_cafe_saas_sdk-0.7.1.dist-info}/METADATA +11 -11
  92. {geek_cafe_saas_sdk-0.6.0.dist-info → geek_cafe_saas_sdk-0.7.1.dist-info}/RECORD +94 -23
  93. {geek_cafe_saas_sdk-0.6.0.dist-info → geek_cafe_saas_sdk-0.7.1.dist-info}/WHEEL +0 -0
  94. {geek_cafe_saas_sdk-0.6.0.dist-info → geek_cafe_saas_sdk-0.7.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,408 @@
1
+ # Subscriptions Domain Handlers
2
+
3
+ Lambda handlers for subscription management operations.
4
+
5
+ ## Directory Structure
6
+
7
+ ```
8
+ handlers/
9
+ ├── plans/
10
+ │ ├── list/app.py # GET /plans
11
+ │ ├── get/app.py # GET /plans/{planId}
12
+ │ ├── create/app.py # POST /plans (admin)
13
+ │ └── update/app.py # PATCH /plans/{planId} (admin)
14
+ ├── addons/
15
+ │ ├── list/app.py # GET /addons
16
+ │ ├── get/app.py # GET /addons/{addonId}
17
+ │ ├── create/app.py # POST /addons (admin)
18
+ │ └── update/app.py # PATCH /addons/{addonId} (admin)
19
+ ├── discounts/
20
+ │ ├── validate/app.py # POST /discounts/validate
21
+ │ ├── create/app.py # POST /discounts (admin)
22
+ │ └── get/app.py # GET /discounts/{discountId} (admin)
23
+ └── usage/
24
+ ├── record/app.py # POST /usage
25
+ └── aggregate/app.py # GET /usage/aggregate
26
+ ```
27
+
28
+ ---
29
+
30
+ ## Plan Handlers
31
+
32
+ ### GET /plans
33
+ List public subscription plans.
34
+
35
+ **Authentication:** None (public endpoint)
36
+
37
+ **Query Parameters:**
38
+ - `status` (string, optional) - Filter by status (default: "active")
39
+ - `isPublic` (boolean, optional) - Filter by visibility (default: true)
40
+ - `limit` (integer, optional) - Max results (default: 50)
41
+
42
+ **Response:** 200 OK
43
+ ```json
44
+ {
45
+ "success": true,
46
+ "data": [
47
+ {
48
+ "id": "plan-123",
49
+ "planCode": "pro",
50
+ "planName": "Pro Plan",
51
+ "priceMonthly Cents": 2999,
52
+ "priceAnnualCents": 29990,
53
+ "features": {...},
54
+ "limits": {...}
55
+ }
56
+ ]
57
+ }
58
+ ```
59
+
60
+ ### GET /plans/{planId}
61
+ Get a specific plan.
62
+
63
+ **Authentication:** None (public endpoint)
64
+
65
+ **Path Parameters:**
66
+ - `planId` (string, required) - Plan ID
67
+
68
+ **Response:** 200 OK
69
+
70
+ ### POST /plans (Admin)
71
+ Create a new plan.
72
+
73
+ **Authentication:** Required (admin)
74
+
75
+ **Request Body:**
76
+ ```json
77
+ {
78
+ "planCode": "enterprise",
79
+ "planName": "Enterprise Plan",
80
+ "priceMonthly Cents": 9999,
81
+ "description": "For large teams",
82
+ "features": {
83
+ "api_access": true,
84
+ "sso": true,
85
+ "white_label": true
86
+ },
87
+ "limits": {
88
+ "max_projects": -1,
89
+ "max_storage_gb": 1000
90
+ }
91
+ }
92
+ ```
93
+
94
+ **Response:** 201 Created
95
+
96
+ ### PATCH /plans/{planId} (Admin)
97
+ Update a plan.
98
+
99
+ **Authentication:** Required (admin)
100
+
101
+ **Request Body:** Fields to update
102
+
103
+ **Response:** 200 OK
104
+
105
+ ---
106
+
107
+ ## Addon Handlers
108
+
109
+ ### GET /addons
110
+ List public addons.
111
+
112
+ **Authentication:** None (public endpoint)
113
+
114
+ **Query Parameters:**
115
+ - `status` (string, optional) - Filter by status
116
+ - `category` (string, optional) - Filter by category
117
+ - `limit` (integer, optional) - Max results
118
+
119
+ **Response:** 200 OK
120
+
121
+ ### GET /addons/{addonId}
122
+ Get a specific addon.
123
+
124
+ **Authentication:** None (public endpoint)
125
+
126
+ **Response:** 200 OK
127
+
128
+ ### POST /addons (Admin)
129
+ Create a new addon.
130
+
131
+ **Authentication:** Required (admin)
132
+
133
+ **Request Body:**
134
+ ```json
135
+ {
136
+ "addonCode": "chat",
137
+ "addonName": "Chat Module",
138
+ "pricingModel": "fixed",
139
+ "priceMonthly Cents": 1500,
140
+ "description": "Real-time chat",
141
+ "category": "communication",
142
+ "features": {
143
+ "realtime_messaging": true,
144
+ "file_sharing": true
145
+ }
146
+ }
147
+ ```
148
+
149
+ **Response:** 201 Created
150
+
151
+ ### PATCH /addons/{addonId} (Admin)
152
+ Update an addon.
153
+
154
+ **Authentication:** Required (admin)
155
+
156
+ **Response:** 200 OK
157
+
158
+ ---
159
+
160
+ ## Discount Handlers
161
+
162
+ ### POST /discounts/validate
163
+ Validate a discount code.
164
+
165
+ **Authentication:** None (public endpoint)
166
+
167
+ **Request Body:**
168
+ ```json
169
+ {
170
+ "discountCode": "SUMMER25",
171
+ "planCode": "pro",
172
+ "amountCents": 2999,
173
+ "isFirstPurchase": false
174
+ }
175
+ ```
176
+
177
+ **Response:** 200 OK (if valid)
178
+ ```json
179
+ {
180
+ "success": true,
181
+ "data": {
182
+ "id": "discount-123",
183
+ "discountCode": "SUMMER25",
184
+ "discountType": "percentage",
185
+ "percentOff": 25.0,
186
+ "duration": "repeating",
187
+ "durationInMonths": 3
188
+ }
189
+ }
190
+ ```
191
+
192
+ **Error Response:** 400 Bad Request (if invalid)
193
+ ```json
194
+ {
195
+ "success": false,
196
+ "message": "Discount code is not currently valid"
197
+ }
198
+ ```
199
+
200
+ ### POST /discounts (Admin)
201
+ Create a new discount.
202
+
203
+ **Authentication:** Required (admin)
204
+
205
+ **Request Body:**
206
+ ```json
207
+ {
208
+ "discountCode": "WELCOME50",
209
+ "discountName": "Welcome Discount",
210
+ "discountType": "percentage",
211
+ "percentOff": 50.0,
212
+ "duration": "once",
213
+ "maxRedemptions": 1000,
214
+ "firstTimeTransaction": true
215
+ }
216
+ ```
217
+
218
+ **Response:** 201 Created
219
+
220
+ ### GET /discounts/{discountId} (Admin)
221
+ Get a discount by ID.
222
+
223
+ **Authentication:** Required (admin)
224
+
225
+ **Response:** 200 OK
226
+
227
+ ---
228
+
229
+ ## Usage Handlers
230
+
231
+ ### POST /usage
232
+ Record a usage event for metered billing.
233
+
234
+ **Authentication:** Required
235
+
236
+ **Request Body:**
237
+ ```json
238
+ {
239
+ "subscriptionId": "sub-123",
240
+ "addonCode": "extra_storage",
241
+ "meterEventName": "storage_gb",
242
+ "quantity": 50.5,
243
+ "action": "increment",
244
+ "idempotencyKey": "unique-key-123",
245
+ "metadata": {
246
+ "source": "api"
247
+ }
248
+ }
249
+ ```
250
+
251
+ **Response:** 201 Created
252
+
253
+ ### GET /usage/aggregate
254
+ Get aggregated usage for a period.
255
+
256
+ **Authentication:** Required
257
+
258
+ **Query Parameters:**
259
+ - `subscriptionId` (string, required)
260
+ - `addonCode` (string, required)
261
+ - `periodStart` (timestamp, required)
262
+ - `periodEnd` (timestamp, required)
263
+
264
+ **Response:** 200 OK
265
+ ```json
266
+ {
267
+ "success": true,
268
+ "data": 150.75
269
+ }
270
+ ```
271
+
272
+ ---
273
+
274
+ ## Authentication
275
+
276
+ **Public Endpoints (no auth):**
277
+ - GET /plans
278
+ - GET /plans/{planId}
279
+ - GET /addons
280
+ - GET /addons/{addonId}
281
+ - POST /discounts/validate
282
+
283
+ **User Endpoints (auth required):**
284
+ - POST /usage
285
+ - GET /usage/aggregate
286
+
287
+ **Admin Endpoints (admin auth required):**
288
+ - POST /plans
289
+ - PATCH /plans/{planId}
290
+ - POST /addons
291
+ - PATCH /addons/{addonId}
292
+ - POST /discounts
293
+ - GET /discounts/{discountId}
294
+
295
+ ---
296
+
297
+ ## Error Handling
298
+
299
+ All handlers return standardized error responses:
300
+
301
+ ```json
302
+ {
303
+ "success": false,
304
+ "message": "Error description",
305
+ "errorCode": "ERROR_CODE"
306
+ }
307
+ ```
308
+
309
+ **Common Error Codes:**
310
+ - `VALIDATION_ERROR` - Invalid input
311
+ - `NOT_FOUND` - Resource not found
312
+ - `INTERNAL_ERROR` - Server error
313
+
314
+ ---
315
+
316
+ ## Deployment
317
+
318
+ Each handler is designed to be deployed as a separate Lambda function with API Gateway integration.
319
+
320
+ ### Example SAM Template
321
+
322
+ ```yaml
323
+ PlanListFunction:
324
+ Type: AWS::Serverless::Function
325
+ Properties:
326
+ CodeUri: src/geek_cafe_saas_sdk/domains/subscriptions/handlers/plans/list/
327
+ Handler: app.handler
328
+ Runtime: python3.11
329
+ Events:
330
+ ListPlans:
331
+ Type: Api
332
+ Properties:
333
+ Path: /plans
334
+ Method: get
335
+ ```
336
+
337
+ ---
338
+
339
+ ## Testing
340
+
341
+ Handlers support dependency injection for testing:
342
+
343
+ ```python
344
+ from handlers.plans.list.app import handler
345
+
346
+ # Mock service
347
+ class MockService:
348
+ def list_plans(self, **kwargs):
349
+ return ServiceResult.success_result([])
350
+
351
+ # Test
352
+ event = {"queryStringParameters": {"status": "active"}}
353
+ response = handler(event, None, injected_service=MockService())
354
+ ```
355
+
356
+ ---
357
+
358
+ ## Request/Response Format
359
+
360
+ ### Request Format
361
+
362
+ Handlers automatically convert camelCase to snake_case:
363
+
364
+ ```json
365
+ {
366
+ "planCode": "pro", // Converted to plan_code
367
+ "priceMonthly Cents": 2999 // Converted to price_monthly_cents
368
+ }
369
+ ```
370
+
371
+ ### Response Format
372
+
373
+ All responses follow the ServiceResult pattern:
374
+
375
+ **Success:**
376
+ ```json
377
+ {
378
+ "success": true,
379
+ "data": {...}
380
+ }
381
+ ```
382
+
383
+ **Error:**
384
+ ```json
385
+ {
386
+ "success": false,
387
+ "message": "Error message",
388
+ "errorCode": "ERROR_CODE"
389
+ }
390
+ ```
391
+
392
+ ---
393
+
394
+ ## Environment Variables
395
+
396
+ Required environment variables:
397
+
398
+ - `DYNAMODB_TABLE_NAME` - DynamoDB table name
399
+ - `AWS_REGION` - AWS region
400
+
401
+ ---
402
+
403
+ ## Notes
404
+
405
+ - Plan and addon handlers are public to allow pricing page display
406
+ - Usage handlers require authentication to prevent abuse
407
+ - Discount validation is public but discount management is admin-only
408
+ - All admin operations should implement proper authorization checks
@@ -0,0 +1 @@
1
+ # Subscriptions Domain Handlers
@@ -0,0 +1,81 @@
1
+ """
2
+ Lambda handler for creating addons.
3
+
4
+ Admin endpoint - requires authentication.
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.subscriptions.services import SubscriptionManagerService
10
+
11
+
12
+ handler_wrapper = create_handler(
13
+ service_class=SubscriptionManagerService,
14
+ require_body=True,
15
+ convert_case=True
16
+ )
17
+
18
+
19
+ def handler(event: Dict[str, Any], context: Any, injected_service=None) -> Dict[str, Any]:
20
+ """
21
+ Create a new addon.
22
+
23
+ Expected body:
24
+ {
25
+ "addonCode": "chat",
26
+ "addonName": "Chat Module",
27
+ "pricingModel": "fixed",
28
+ "priceMonthly Cents": 1500,
29
+ "description": "Real-time chat for your app",
30
+ "category": "communication"
31
+ }
32
+
33
+ Returns 201 with created addon
34
+ """
35
+ return handler_wrapper.execute(event, context, create_addon, injected_service)
36
+
37
+
38
+ def create_addon(
39
+ event: Dict[str, Any],
40
+ service: SubscriptionManagerService,
41
+ user_context: Dict[str, str]
42
+ ) -> Any:
43
+ """
44
+ Business logic for creating an addon.
45
+ """
46
+ payload = event["parsed_body"]
47
+
48
+ # Extract required fields
49
+ addon_code = payload.get("addon_code")
50
+ if not addon_code:
51
+ raise ValueError("addon_code is required")
52
+
53
+ addon_name = payload.get("addon_name")
54
+ if not addon_name:
55
+ raise ValueError("addon_name is required")
56
+
57
+ pricing_model = payload.get("pricing_model", "fixed")
58
+
59
+ # Build kwargs for optional fields
60
+ kwargs = {}
61
+ optional_fields = [
62
+ "description", "category", "status", "is_public", "sort_order",
63
+ "price_monthly_cents", "price_annual_cents", "currency",
64
+ "price_per_unit_cents", "unit_name", "included_units", "min_units", "max_units",
65
+ "pricing_tiers", "trial_days", "features", "limits",
66
+ "compatible_plan_codes", "incompatible_addon_codes", "feature_list",
67
+ "icon", "color", "is_metered", "meter_event_name", "billing_scheme"
68
+ ]
69
+
70
+ for field in optional_fields:
71
+ if field in payload:
72
+ kwargs[field] = payload[field]
73
+
74
+ result = service.create_addon(
75
+ addon_code=addon_code,
76
+ addon_name=addon_name,
77
+ pricing_model=pricing_model,
78
+ **kwargs
79
+ )
80
+
81
+ return result
@@ -0,0 +1,48 @@
1
+ """
2
+ Lambda handler for getting an addon.
3
+
4
+ Public endpoint - no authentication required.
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.subscriptions.services import SubscriptionManagerService
10
+
11
+
12
+ handler_wrapper = create_handler(
13
+ service_class=SubscriptionManagerService,
14
+ require_auth=False,
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 an addon by ID.
23
+
24
+ Path parameters:
25
+ - addonId: Addon ID
26
+
27
+ Returns 200 with addon details
28
+ """
29
+ return handler_wrapper.execute(event, context, get_addon, injected_service)
30
+
31
+
32
+ def get_addon(
33
+ event: Dict[str, Any],
34
+ service: SubscriptionManagerService,
35
+ user_context: Dict[str, str]
36
+ ) -> Any:
37
+ """
38
+ Business logic for getting an addon.
39
+ """
40
+ path_params = event.get("pathParameters") or {}
41
+ addon_id = path_params.get("addon_id")
42
+
43
+ if not addon_id:
44
+ raise ValueError("addon_id is required in path")
45
+
46
+ result = service.get_addon(addon_id=addon_id)
47
+
48
+ return result
@@ -0,0 +1,54 @@
1
+ """
2
+ Lambda handler for listing addons.
3
+
4
+ Public endpoint - no authentication required.
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.subscriptions.services import SubscriptionManagerService
10
+
11
+
12
+ handler_wrapper = create_handler(
13
+ service_class=SubscriptionManagerService,
14
+ require_auth=False,
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 public addons.
23
+
24
+ Query parameters:
25
+ - status: Filter by status (default: "active")
26
+ - category: Filter by category
27
+ - limit: Max results (default: 50)
28
+
29
+ Returns 200 with list of addons
30
+ """
31
+ return handler_wrapper.execute(event, context, list_addons, injected_service)
32
+
33
+
34
+ def list_addons(
35
+ event: Dict[str, Any],
36
+ service: SubscriptionManagerService,
37
+ user_context: Dict[str, str]
38
+ ) -> Any:
39
+ """
40
+ Business logic for listing addons.
41
+ """
42
+ params = event.get("queryStringParameters") or {}
43
+
44
+ status = params.get("status", "active")
45
+ category = params.get("category")
46
+ limit = int(params.get("limit", "50"))
47
+
48
+ result = service.list_addons(
49
+ status=status,
50
+ category=category,
51
+ limit=limit
52
+ )
53
+
54
+ return result
@@ -0,0 +1,54 @@
1
+ """
2
+ Lambda handler for updating addons.
3
+
4
+ Admin endpoint - requires authentication.
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.subscriptions.services import SubscriptionManagerService
10
+
11
+
12
+ handler_wrapper = create_handler(
13
+ service_class=SubscriptionManagerService,
14
+ require_body=True,
15
+ convert_case=True
16
+ )
17
+
18
+
19
+ def handler(event: Dict[str, Any], context: Any, injected_service=None) -> Dict[str, Any]:
20
+ """
21
+ Update an addon.
22
+
23
+ Path parameters:
24
+ - addonId: Addon ID
25
+
26
+ Body contains fields to update
27
+
28
+ Returns 200 with updated addon
29
+ """
30
+ return handler_wrapper.execute(event, context, update_addon, injected_service)
31
+
32
+
33
+ def update_addon(
34
+ event: Dict[str, Any],
35
+ service: SubscriptionManagerService,
36
+ user_context: Dict[str, str]
37
+ ) -> Any:
38
+ """
39
+ Business logic for updating an addon.
40
+ """
41
+ path_params = event.get("pathParameters") or {}
42
+ addon_id = path_params.get("addon_id")
43
+
44
+ if not addon_id:
45
+ raise ValueError("addon_id is required in path")
46
+
47
+ payload = event["parsed_body"]
48
+
49
+ result = service.update_addon(
50
+ addon_id=addon_id,
51
+ updates=payload
52
+ )
53
+
54
+ return result