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,151 @@
1
+ # DynamoDB Utilities
2
+
3
+ from typing import Dict, Set
4
+
5
+ # DynamoDB Reserved Keywords (subset of common ones)
6
+ # Full list: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html
7
+ DYNAMODB_RESERVED_KEYWORDS: Set[str] = {
8
+ 'ABORT', 'ABSOLUTE', 'ACTION', 'ADD', 'AFTER', 'AGENT', 'AGGREGATE', 'ALL', 'ALSO', 'ALTER',
9
+ 'ANALYZE', 'AND', 'ANY', 'ARCHIVE', 'ARE', 'ARRAY', 'AS', 'ASC', 'ASSERT', 'AST', 'AT',
10
+ 'AUTHORIZATION', 'BACKUP', 'BATCH', 'BEFORE', 'BEGIN', 'BETWEEN', 'BIGINT', 'BINARY', 'BIT',
11
+ 'BLOB', 'BLOCK', 'BOOLEAN', 'BOTH', 'BREAK', 'BUCKET', 'BULK', 'BY', 'BYTE', 'CALL', 'CALLED',
12
+ 'CALLING', 'CAPACITY', 'CASCADE', 'CASCADED', 'CASE', 'CAST', 'CATALOG', 'CHAIN', 'CHANGE',
13
+ 'CHANGED', 'CHARACTER', 'CHECK', 'CLASS', 'CLOB', 'CLOSE', 'CLUSTER', 'CLUSTERED', 'CLUSTERING',
14
+ 'CLUSTERS', 'COALESCE', 'COLLATE', 'COLLATION', 'COLUMN', 'COLUMNS', 'COMBINE', 'COMMENT',
15
+ 'COMMIT', 'COMMITTED', 'COMPACT', 'COMPILE', 'COMPILED', 'CONCURRENTLY', 'CONDITION',
16
+ 'CONDITIONAL', 'CONFLICT', 'CONNECT', 'CONNECTION', 'CONSTRAINT', 'CONTAINS', 'CONVERT',
17
+ 'COPY', 'COST', 'CREATE', 'CROSS', 'CSV', 'CUBE', 'CURRENT', 'CURSOR', 'CYCLE', 'DATA',
18
+ 'DATABASE', 'DATABASES', 'DATE', 'DATETIME', 'DAY', 'DEALLOCATE', 'DEC', 'DECIMAL', 'DECLARE',
19
+ 'DEFAULT', 'DEFAULTS', 'DEFERRABLE', 'DEFERRED', 'DEFINE', 'DEFINED', 'DEFINITION', 'DELETE',
20
+ 'DELIMITER', 'DELIMITERS', 'DENSE_RANK', 'DEREF', 'DESC', 'DETACH', 'DETERMINISTIC', 'DICTIONARY',
21
+ 'DISABLE', 'DISCARD', 'DISTINCT', 'DO', 'DOCUMENT', 'DOMAIN', 'DOUBLE', 'DROP', 'EACH', 'ELEMENT',
22
+ 'ELSE', 'EMPTY', 'ENABLE', 'ENCODING', 'ENCRYPTED', 'END', 'ENUM', 'EQUAL', 'EQUALS', 'ERROR',
23
+ 'ERRORS', 'ESCAPE', 'EVENT', 'EXCEPT', 'EXCEPTION', 'EXCEPTIONS', 'EXCLUDE', 'EXCLUDING',
24
+ 'EXCLUSIVE', 'EXECUTE', 'EXISTS', 'EXPLAIN', 'EXPRESSION', 'EXTENDED', 'EXTENDS', 'EXTERNAL',
25
+ 'EXTRACT', 'FALSE', 'FAMILY', 'FETCH', 'FILTER', 'FIRST', 'FLOAT', 'FOLLOWING', 'FOR', 'FORCE',
26
+ 'FOREIGN', 'FORMAT', 'FORWARD', 'FOUNDATION', 'FRAME', 'FREE', 'FROM', 'FULL', 'FUNCTION',
27
+ 'FUNCTIONS', 'GENERAL', 'GENERATED', 'GET', 'GLOBAL', 'GO', 'GOTO', 'GRANT', 'GRANTED', 'GREATEST',
28
+ 'GROUP', 'GROUPING', 'GROUPS', 'HANDLER', 'HAVING', 'HEADER', 'HOLD', 'HOUR', 'IDENTITY', 'IF',
29
+ 'ILIKE', 'IMMEDIATE', 'IMMUTABLE', 'IMPLICIT', 'IMPORT', 'IN', 'INCLUDING', 'INCREMENT',
30
+ 'INCREMENTAL', 'INDEX', 'INDEXED', 'INDEXES', 'INDICATE', 'INHERITS', 'INITIALLY', 'INLINE',
31
+ 'INNER', 'INOUT', 'INPUT', 'INSENSITIVE', 'INSERT', 'INSTEAD', 'INT', 'INTEGER', 'INTERSECT',
32
+ 'INTERVAL', 'INTO', 'INVOKER', 'IS', 'ISNULL', 'ISOLATION', 'ITEM', 'ITEMS', 'ITERATE', 'JOIN',
33
+ 'KEY', 'KEYS', 'LABEL', 'LANGUAGE', 'LARGE', 'LAST', 'LATERAL', 'LEAD', 'LEADING', 'LEAKPROOF',
34
+ 'LEAST', 'LEFT', 'LENGTH', 'LEVEL', 'LIKE', 'LIMIT', 'LISTEN', 'LOAD', 'LOCAL', 'LOCALTIME',
35
+ 'LOCALTIMESTAMP', 'LOCATION', 'LOCK', 'LOCKS', 'LOGGED', 'MAPPING', 'MATCH', 'MATERIALIZED',
36
+ 'MAXVALUE', 'MINUS', 'MINUTE', 'MINVALUE', 'MODE', 'MODIFIES', 'MODIFY', 'MONTH', 'MOVE',
37
+ 'NAME', 'NAMES', 'NATIONAL', 'NATURAL', 'NCHAR', 'NCLOB', 'NESTED', 'NEW', 'NEXT', 'NO',
38
+ 'NONE', 'NOT', 'NOTHING', 'NOTIFY', 'NOTNULL', 'NOWAIT', 'NULL', 'NULLIF', 'NULLS', 'NUMBER',
39
+ 'NUMERIC', 'OBJECT', 'OF', 'OFF', 'OFFSET', 'OIDS', 'OLD', 'ON', 'ONLY', 'OPERATOR', 'OPTION',
40
+ 'OPTIONS', 'OR', 'ORDER', 'ORDINALITY', 'OTHERS', 'OUT', 'OUTER', 'OVER', 'OVERLAPS', 'OVERLAY',
41
+ 'OVERRIDING', 'OWNED', 'OWNER', 'PARSER', 'PARTIAL', 'PARTITION', 'PARTITIONED', 'PARTITIONS',
42
+ 'PASSING', 'PASSWORD', 'PLACING', 'PLANS', 'POLICY', 'POSITION', 'PRECEDING', 'PRECISION',
43
+ 'PREPARE', 'PREPARED', 'PRESERVE', 'PRIMARY', 'PRIOR', 'PRIVILEGES', 'PROCEDURAL', 'PROCEDURE',
44
+ 'PROGRAM', 'QUOTE', 'RANGE', 'RANK', 'READ', 'READS', 'REAL', 'REASSIGN', 'RECHECK', 'RECURSIVE',
45
+ 'REF', 'REFERENCES', 'REFERENCING', 'REFRESH', 'REINDEX', 'RELATIVE', 'RELEASE', 'RENAME',
46
+ 'REPEATABLE', 'REPLACE', 'REPLICA', 'RESET', 'RESTART', 'RESTRICT', 'RETURNING', 'RETURNS',
47
+ 'REVOKE', 'RIGHT', 'ROLE', 'ROLES', 'ROLLBACK', 'ROLLUP', 'ROUTINE', 'ROUTINES', 'ROW',
48
+ 'ROWS', 'RULE', 'SAVEPOINT', 'SCALE', 'SCHEMA', 'SCHEMAS', 'SCROLL', 'SEARCH', 'SECOND',
49
+ 'SECURITY', 'SELECT', 'SEQUENCE', 'SEQUENCES', 'SERIALIZABLE', 'SERVER', 'SESSION', 'SET',
50
+ 'SETS', 'SHARE', 'SHOW', 'SIMILAR', 'SIMPLE', 'SMALLINT', 'SNAPSHOT', 'SOME', 'SQL', 'STABLE',
51
+ 'STANDALONE', 'START', 'STATEMENT', 'STATISTICS', 'STDIN', 'STDOUT', 'STORAGE', 'STORED',
52
+ 'STRICT', 'STRIP', 'SUBSTRING', 'SYMMETRIC', 'SYSID', 'SYSTEM', 'TABLE', 'TABLES', 'TABLESPACE',
53
+ 'TEMP', 'TEMPLATE', 'TEMPORARY', 'TEXT', 'THEN', 'TIME', 'TIMESTAMP', 'TO', 'TRAILING',
54
+ 'TRANSACTION', 'TRANSFORM', 'TREAT', 'TRIGGER', 'TRIM', 'TRUE', 'TRUNCATE', 'TRUSTED', 'TYPE',
55
+ 'TYPES', 'UESCAPE', 'UNBOUNDED', 'UNCOMMITTED', 'UNENCRYPTED', 'UNION', 'UNIQUE', 'UNKNOWN',
56
+ 'UNLISTEN', 'UNLOGGED', 'UNTIL', 'UPDATE', 'USER', 'USERS', 'USING', 'VACUUM', 'VALID',
57
+ 'VALIDATE', 'VALIDATOR', 'VALUE', 'VALUES', 'VARCHAR', 'VARIADIC', 'VARYING', 'VERBOSE',
58
+ 'VERSION', 'VIEW', 'VIEWS', 'VOLATILE', 'WHEN', 'WHERE', 'WHITESPACE', 'WINDOW', 'WITH',
59
+ 'WITHIN', 'WITHOUT', 'WORK', 'WRAPPER', 'WRITE', 'XML', 'XMLATTRIBUTES', 'XMLCONCAT', 'XMLELEMENT',
60
+ 'XMLEXISTS', 'XMLFOREST', 'XMLPARSE', 'XMLPI', 'XMLROOT', 'XMLSERIALIZE', 'YEAR', 'YES', 'ZONE'
61
+ }
62
+
63
+
64
+ def build_expression_attribute_names(attributes: list[str]) -> Dict[str, str]:
65
+ """
66
+ Build ExpressionAttributeNames mapping for DynamoDB reserved keywords.
67
+
68
+ Args:
69
+ attributes: List of attribute names that may contain reserved keywords
70
+
71
+ Returns:
72
+ Dictionary mapping attribute placeholders to actual attribute names
73
+
74
+ Example:
75
+ >>> build_expression_attribute_names(['email', 'roles', 'name'])
76
+ {'#email': 'email', '#roles': 'roles', '#name': 'name'}
77
+ """
78
+ expression_attribute_names = {}
79
+
80
+ for attr in attributes:
81
+ if attr.upper() in DYNAMODB_RESERVED_KEYWORDS:
82
+ # Use placeholder for reserved keywords
83
+ placeholder = f"#{attr}"
84
+ expression_attribute_names[placeholder] = attr
85
+ else:
86
+ # For non-reserved keywords, we could still use placeholders for consistency
87
+ # but it's optional. For now, we'll only add placeholders for reserved keywords.
88
+ pass
89
+
90
+ return expression_attribute_names
91
+
92
+
93
+ def replace_reserved_keywords_in_expression(expression: str, attribute_names: Dict[str, str]) -> str:
94
+ """
95
+ Replace reserved keyword attribute names with placeholders in expressions.
96
+
97
+ Args:
98
+ expression: The expression string (e.g., ProjectionExpression)
99
+ attribute_names: The ExpressionAttributeNames mapping
100
+
101
+ Returns:
102
+ Expression with reserved keywords replaced by placeholders
103
+
104
+ Example:
105
+ >>> replace_reserved_keywords_in_expression(
106
+ ... "pk, sk, email, roles, name",
107
+ ... {'#email': 'email', '#roles': 'roles'}
108
+ ... )
109
+ "pk, sk, #email, #roles, name"
110
+ """
111
+ result = expression
112
+
113
+ # Replace each reserved keyword with its placeholder
114
+ for placeholder, attr_name in attribute_names.items():
115
+ result = result.replace(attr_name, placeholder)
116
+
117
+ return result
118
+
119
+
120
+ def build_projection_with_reserved_keywords(attributes: list[str]) -> tuple[str, Dict[str, str]]:
121
+ """
122
+ Convenience function to build projection expression and attribute names mapping.
123
+
124
+ Args:
125
+ attributes: List of attribute names for projection
126
+
127
+ Returns:
128
+ Tuple of (projection_expression, expression_attribute_names)
129
+
130
+ Example:
131
+ >>> projection, attr_names = build_projection_with_reserved_keywords(
132
+ ... ['pk', 'sk', 'email', 'roles', 'name']
133
+ ... )
134
+ >>> projection
135
+ "pk, sk, #email, #roles, name"
136
+ >>> attr_names
137
+ {'#email': 'email', '#roles': 'roles'}
138
+ """
139
+ # Build the attribute names mapping
140
+ expression_attribute_names = build_expression_attribute_names(attributes)
141
+
142
+ # Create projection expression
143
+ projection_expression = ", ".join(attributes)
144
+
145
+ # Replace reserved keywords with placeholders
146
+ if expression_attribute_names:
147
+ projection_expression = replace_reserved_keywords_in_expression(
148
+ projection_expression, expression_attribute_names
149
+ )
150
+
151
+ return projection_expression, expression_attribute_names
@@ -0,0 +1,149 @@
1
+ """
2
+ Geek Cafe SaaS Services Environment Services.
3
+
4
+ This module provides utilities for loading and accessing environment variables
5
+ used throughout the Geek Cafe SaaS Services application. It includes classes for
6
+ loading environment files and accessing specific environment variables in a
7
+ consistent manner.
8
+ """
9
+
10
+ import os
11
+ import json
12
+ from typing import Dict, List, Any, Optional
13
+ from pathlib import Path
14
+ from dotenv import load_dotenv
15
+ from aws_lambda_powertools import Logger
16
+
17
+
18
+ logger = Logger(__name__)
19
+
20
+ DEBUGGING = os.getenv("DEBUGGING", "false").lower() == "true"
21
+
22
+
23
+ class EnvironmentLoader:
24
+ """Utility class for loading environment variables from files.
25
+
26
+ This class provides methods to load environment variables from .env files,
27
+ load event files for testing, and search for files in the project directory structure.
28
+ """
29
+
30
+ def load_environment(
31
+ self,
32
+ *,
33
+ starting_path: Optional[str] = None,
34
+ file_name: str = ".env.dev",
35
+ override_vars: bool = True,
36
+ raise_error_if_not_found: bool = True,
37
+ ) -> None:
38
+ """Load environment variables from a .env file.
39
+
40
+ Searches for the specified environment file starting from the given path
41
+ and loads the environment variables from it.
42
+
43
+ Args:
44
+ starting_path: Path to start searching from. If None, uses the current file's location.
45
+ file_name: Name of the environment file to load (default: ".env.dev").
46
+ override_vars: Whether to override existing environment variables (default: True).
47
+ raise_error_if_not_found: Whether to raise an error if the file is not found (default: True).
48
+
49
+ Raises:
50
+ RuntimeError: If the environment file is not found and raise_error_if_not_found is True.
51
+ """
52
+
53
+ if not starting_path:
54
+ starting_path = __file__
55
+
56
+ environment_file: str | None = self.find_file(
57
+ starting_path=starting_path,
58
+ file_name=file_name,
59
+ raise_error_if_not_found=raise_error_if_not_found,
60
+ )
61
+
62
+ if environment_file:
63
+ load_dotenv(dotenv_path=environment_file, override=override_vars)
64
+
65
+ if DEBUGGING:
66
+ env_vars = os.environ
67
+ logger.debug(f"Loaded environment file: {environment_file}")
68
+ # print(env_vars)
69
+
70
+ def load_event_file(self, full_path: str) -> Dict[str, Any]:
71
+ """Load and parse a JSON event file.
72
+
73
+ Loads a JSON event file and handles common event structures by extracting
74
+ the actual event data from nested 'message' or 'event' fields if present.
75
+
76
+ Args:
77
+ full_path: The full path to the JSON event file.
78
+
79
+ Returns:
80
+ The parsed event data as a dictionary.
81
+
82
+ Raises:
83
+ RuntimeError: If the event file does not exist.
84
+ """
85
+ if not os.path.exists(full_path):
86
+ raise RuntimeError(f"Failed to locate event file: {full_path}")
87
+
88
+ event: Dict = {}
89
+ with open(full_path, mode="r", encoding="utf-8") as json_file:
90
+ event = json.load(json_file)
91
+
92
+ if "message" in event:
93
+ tmp = event.get("message")
94
+ if isinstance(tmp, Dict):
95
+ event = tmp
96
+
97
+ if "event" in event:
98
+ tmp = event.get("event")
99
+ if isinstance(tmp, Dict):
100
+ event = tmp
101
+
102
+ return event
103
+
104
+ def find_file(
105
+ self, starting_path: str, file_name: str, raise_error_if_not_found: bool = True, max_parent_directories: int = 25
106
+ ) -> Optional[str]:
107
+ """Search for a file in the project directory structure.
108
+
109
+ Searches for the specified file by traversing up the directory tree
110
+ starting from the given path, up to a maximum number of parent directories.
111
+
112
+ Args:
113
+ starting_path: Path to start searching from.
114
+ file_name: Name of the file to search for.
115
+ raise_error_if_not_found: Whether to raise an error if the file is not found (default: True).
116
+ max_parent_directories: Maximum number of parent directories to search (default: 25).
117
+
118
+ Returns:
119
+ The full path to the found file, or None if not found and raise_error_if_not_found is False.
120
+
121
+ Raises:
122
+ RuntimeError: If the file is not found and raise_error_if_not_found is True.
123
+ """
124
+ parents = max_parent_directories
125
+ starting_path = starting_path or __file__
126
+
127
+ paths: List[str] = []
128
+ for parent in range(parents):
129
+ # Check if we have enough parent directories available
130
+ current_path = Path(starting_path)
131
+ if parent >= len(current_path.parents):
132
+ break
133
+
134
+ path = current_path.parents[parent].absolute()
135
+ print(f"searching: {path}")
136
+ tmp = os.path.join(path, file_name)
137
+ paths.append(tmp)
138
+ if os.path.exists(tmp):
139
+ return tmp
140
+
141
+ if raise_error_if_not_found:
142
+ searched_paths = "\n".join(paths)
143
+ raise RuntimeError(
144
+ f"Failed to locate environment file: {file_name} in: \n {searched_paths}"
145
+ )
146
+
147
+ return None
148
+
149
+
@@ -0,0 +1,228 @@
1
+ """
2
+ Geek Cafe SaaS Services Environment Services.
3
+
4
+ This module provides utilities for loading and accessing environment variables
5
+ used throughout the Geek Cafe SaaS Services application. It includes classes for
6
+ loading environment files and accessing specific environment variables in a
7
+ consistent manner.
8
+ """
9
+
10
+ import os
11
+ from typing import Optional
12
+ from aws_lambda_powertools import Logger
13
+
14
+
15
+ logger = Logger(__name__)
16
+
17
+ DEBUGGING = os.getenv("DEBUGGING", "false").lower() == "true"
18
+
19
+
20
+
21
+
22
+
23
+ class EnvironmentVariables:
24
+ """
25
+ Centralized access to environment variables used throughout the application.
26
+
27
+ This class provides static methods to access environment variables in a consistent manner,
28
+ with proper typing and default values where appropriate. Using this class instead of direct
29
+ os.getenv calls helps track and manage all environment variables in one place, making
30
+ maintenance and documentation easier.
31
+ """
32
+
33
+ @staticmethod
34
+ def get_aws_region() -> Optional[str]:
35
+ """
36
+ Get the AWS region from environment variables.
37
+
38
+ Returns:
39
+ The AWS region as a string, or None if not set.
40
+ """
41
+ value = os.getenv("AWS_REGION")
42
+ return value
43
+
44
+ @staticmethod
45
+ def get_aws_profile() -> Optional[str]:
46
+ """
47
+ Get the AWS profile used for CLI/boto3 commands.
48
+
49
+ This should only be set with temporary credentials and only for development purposes.
50
+
51
+ Returns:
52
+ The AWS profile name as a string, or None if not set.
53
+ """
54
+ value = os.getenv("AWS_PROFILE")
55
+ return value
56
+
57
+ @staticmethod
58
+ def get_aws_account_id() -> Optional[str]:
59
+ """
60
+ Get the AWS account ID from environment variables.
61
+
62
+ Returns:
63
+ The AWS account ID as a string, or None if not set.
64
+ """
65
+ value = os.getenv("AWS_ACCOUNT_ID")
66
+ return value
67
+
68
+ @staticmethod
69
+ def get_auth_target_validation_level() -> Optional[str]:
70
+ """
71
+ Get the authentication target validation level from environment variables.
72
+
73
+ Validation levels:
74
+ PASS_THROUGH: Allows the logged in user to be listed as the target
75
+ if the target user isn't explicitly listed. This provides backward compatibility
76
+ during conversion from short URLs to more detailed URL routes.
77
+
78
+ STRICT: Requires a target user/tenant to be explicitly specified in the path.
79
+ This will be required for all new endpoints. The endpoints have been created
80
+ but some UI and tests have not been updated yet.
81
+
82
+ Returns:
83
+ The validation level as a string ("PASS_THROUGH" or "STRICT"), or None if not set.
84
+ """
85
+ value = os.getenv("AUTH_TARGET_VALIDATION_LEVEL")
86
+ return value
87
+
88
+
89
+
90
+ @staticmethod
91
+ def get_logging_level(default: str = "INFO") -> str:
92
+ """
93
+ Get the logging level from environment variables.
94
+
95
+ Args:
96
+ default: Default logging level to use if not set in environment (default: "INFO").
97
+
98
+ Returns:
99
+ The logging level as a string.
100
+ """
101
+ value = os.getenv("LOG_LEVEL", default)
102
+ return value
103
+
104
+ @staticmethod
105
+ def get_app_domain():
106
+ """
107
+ gets the app domain name from an environment var
108
+ """
109
+ value = os.getenv("APP_DOMAIN")
110
+ return value
111
+
112
+ @staticmethod
113
+ def get_ses_user_name():
114
+ """
115
+ gets the ses user-name from an environment var
116
+ """
117
+ value = os.getenv("SES_USER_NAME")
118
+ return value
119
+
120
+ @staticmethod
121
+ def get_ses_password():
122
+ """
123
+ gets the ses password from an environment var
124
+ """
125
+ value = os.getenv("SES_PASSWORD")
126
+ return value
127
+
128
+ @staticmethod
129
+ def get_ses_endpoint():
130
+ """
131
+ gets the ses endpoint from an environment var
132
+ """
133
+ value = os.getenv("SES_END_POINT")
134
+ return value
135
+
136
+ @staticmethod
137
+ def get_cognito_user_pool() -> str | None:
138
+ """
139
+ gets the cognito user pool from an environment var
140
+ """
141
+ value = os.getenv("COGNITO_USER_POOL")
142
+ return value
143
+
144
+ @staticmethod
145
+ def get_dynamodb_table_name():
146
+ """
147
+ gets the dynamodb table name from an environment var
148
+ """
149
+ value = os.getenv("APPLICATION_TABLE_NAME")
150
+ return value
151
+
152
+ @staticmethod
153
+ def get_dynamodb_raise_on_error_setting() -> bool:
154
+ """
155
+ gets the dynamodb table name from an environment var
156
+ """
157
+ value = str(os.getenv("RAISE_ON_DB_ERROR", "true")).lower() == "true"
158
+
159
+ return value
160
+
161
+ @staticmethod
162
+ def get_tenant_user_file_bucket_name():
163
+ """
164
+ gets the tenant user file bucket name from an environment var
165
+ """
166
+ value = os.getenv("TENANT_USER_FILE_BUCKET")
167
+ return value
168
+
169
+ @staticmethod
170
+ def get_tenant_user_upload_bucket_name():
171
+ """
172
+ gets the tenant user upload bucket name from an environment var
173
+ """
174
+ value = os.getenv("UPLOAD_BUCKET")
175
+ return value
176
+
177
+ @staticmethod
178
+ def get_lambda_function_to_invoke() -> str | None:
179
+ """
180
+ gets the lambda function to invoke from an environment var
181
+ this is used by sync to async lambda invocation, or by the queue
182
+ """
183
+ value = os.getenv("LAMBDA_FUNCTION_TO_INVOKE")
184
+ return value
185
+
186
+ @staticmethod
187
+ def get_amazon_trace_id():
188
+ """
189
+ gets the amazon trace id from an environment var
190
+ """
191
+ value = os.getenv("_X_AMZN_TRACE_ID", "NA")
192
+ return value
193
+
194
+ @staticmethod
195
+ def get_integration_tests_setting() -> bool:
196
+ """
197
+ determine if integration tests are run from an environment var
198
+ """
199
+ value = str(os.getenv("RUN_INTEGRATION_TESTS", "False")).lower() == "true"
200
+ env = EnvironmentVariables.get_environment_setting()
201
+
202
+ if env.lower().startswith("prod"):
203
+ value = False
204
+
205
+ return value
206
+
207
+ @staticmethod
208
+ def get_environment_setting() -> str:
209
+ """
210
+ gets the environment name from an environment var
211
+ """
212
+ value = os.getenv("ENVIRONMENT") or os.getenv("ENVIRONMENT_NAME")
213
+
214
+ if not value:
215
+ logger.warning(
216
+ "ENVIRONMENT var is not set. A future version will throw an error."
217
+ )
218
+ return ""
219
+
220
+ return value
221
+
222
+ @staticmethod
223
+ def is_development_environment() -> bool:
224
+ """
225
+ determine if the environment is development
226
+ """
227
+ env = EnvironmentVariables.get_environment_setting()
228
+ return env.lower().startswith("dev")
@@ -0,0 +1,44 @@
1
+ from geek_cafe_saas_sdk.utilities.lambda_event_utility import LambdaEventUtility
2
+
3
+
4
+ class HttpBodyParameters:
5
+ """Search Http QueryString Parameters"""
6
+
7
+ def start_date(self, event: dict) -> str | None:
8
+ """A start date path parameter (start-date)"""
9
+ return self.find(event, "start-date")
10
+
11
+ def end_date(self, event: dict) -> str | None:
12
+ """An end date path parameter (end-date)"""
13
+ return self.find(event, "end-date")
14
+
15
+ def user_id(self, event: dict) -> str | None:
16
+ """The userId path parameter (user-id)"""
17
+ return self.find(event, "user-id")
18
+
19
+ def status(self, event: dict) -> str | None:
20
+ """The status parameter"""
21
+ return self.find(event, "status")
22
+
23
+ def type(self, event: dict) -> str | None:
24
+ """The type parameter"""
25
+ return self.find(event, "type")
26
+
27
+ def file_id(self, event: dict) -> str | None:
28
+ """The file id parameter"""
29
+ return self.find(event, "file-id")
30
+
31
+ def tenant_id(self, event: dict) -> str | None:
32
+ """The tenant id parameter"""
33
+ return self.find(event, "tenant-id")
34
+
35
+ def subscription_id(self, event: dict) -> str | None:
36
+ """The subscription id parameter"""
37
+ return self.find(event, "subscription-id")
38
+
39
+ def find(self, event: dict, key: str) -> str | None:
40
+ """Generic Search/Find a key in the path parameters"""
41
+ value = LambdaEventUtility.get_value_from_event(event, key)
42
+ if isinstance(value, str):
43
+ return value
44
+ return None
@@ -0,0 +1,60 @@
1
+ """
2
+ Geek Cafe SaaS Services Http Path Parameters
3
+ """
4
+
5
+ import os
6
+ from .lambda_event_utility import LambdaEventUtility
7
+
8
+ DEFAULT_RESPONSE_LIMIT = os.getenv("DEFAULT_RESPONSE_LIMIT")
9
+
10
+
11
+ class HttpPathParameters:
12
+ """Search Http Path Parameters"""
13
+
14
+ def start_date(self, event: dict) -> str | None:
15
+ """A start date path parameter (start-date)"""
16
+ return self.find(event, "start-date")
17
+
18
+ def end_date(self, event: dict) -> str | None:
19
+ """An end date path parameter (end-date)"""
20
+ return self.find(event, "end-date")
21
+
22
+ def user_id(self, event: dict) -> str | None:
23
+ """The userId path parameter (user-id)"""
24
+ return self.find(event, "user-id")
25
+
26
+ def status(self, event: dict) -> str | None:
27
+ """The status parameter"""
28
+ return self.find(event, "status")
29
+
30
+ def file_id(self, event: dict) -> str | None:
31
+ """The file id parameter"""
32
+ return self.find(event, "file-id")
33
+
34
+ def tenant_id(self, event: dict) -> str | None:
35
+ """The tenant id parameter"""
36
+ return self.find(event, "tenant-id")
37
+
38
+ def subscription_id(self, event: dict) -> str | None:
39
+ """The subscription id parameter"""
40
+ return self.find(event, "subscription-id")
41
+
42
+ def find(self, event: dict, key: str) -> str | None:
43
+ """Generic Search/Find a key in the path parameters"""
44
+ value = LambdaEventUtility.get_value_from_path_parameters(event, key)
45
+ if isinstance(value, str):
46
+ return value
47
+ return None
48
+
49
+ def limit(self, event: dict) -> int | None:
50
+ """
51
+ Returns the limit if any. Used for response limits on datasets
52
+ responses from DynamoDB
53
+ """
54
+ value = self.find(event, "limit")
55
+ if isinstance(value, str):
56
+ try:
57
+ return int(value)
58
+ except: # noqa: E722, pylint: disable=w0702
59
+ pass
60
+ return None