velocity-python 0.0.169__tar.gz → 0.0.171__tar.gz

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.
Files changed (142) hide show
  1. {velocity_python-0.0.169 → velocity_python-0.0.171}/PKG-INFO +1 -1
  2. {velocity_python-0.0.169 → velocity_python-0.0.171}/pyproject.toml +1 -1
  3. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/__init__.py +1 -1
  4. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/aws/handlers/base_handler.py +20 -23
  5. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/aws/handlers/context.py +5 -6
  6. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/aws/handlers/lambda_handler.py +123 -122
  7. velocity_python-0.0.171/src/velocity/aws/handlers/mixins/__init__.py +10 -0
  8. velocity_python-0.0.171/src/velocity/aws/handlers/mixins/web_handler.py +357 -0
  9. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/aws/handlers/sqs_handler.py +1 -1
  10. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity_python.egg-info/PKG-INFO +1 -1
  11. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity_python.egg-info/SOURCES.txt +2 -4
  12. velocity_python-0.0.171/tests/test_mixins_import.py +14 -0
  13. velocity_python-0.0.169/src/velocity/aws/handlers/mixins/__init__.py +0 -14
  14. velocity_python-0.0.169/src/velocity/aws/handlers/mixins/activity_tracker.py +0 -181
  15. velocity_python-0.0.169/src/velocity/aws/handlers/mixins/enhanced_handler_mixin.py +0 -55
  16. velocity_python-0.0.169/src/velocity/aws/handlers/mixins/error_handler.py +0 -206
  17. velocity_python-0.0.169/src/velocity/aws/handlers/mixins/legacy_mixin.py +0 -55
  18. {velocity_python-0.0.169 → velocity_python-0.0.171}/LICENSE +0 -0
  19. {velocity_python-0.0.169 → velocity_python-0.0.171}/README.md +0 -0
  20. {velocity_python-0.0.169 → velocity_python-0.0.171}/setup.cfg +0 -0
  21. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/app/__init__.py +0 -0
  22. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/app/invoices.py +0 -0
  23. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/app/orders.py +0 -0
  24. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/app/payments.py +0 -0
  25. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/app/purchase_orders.py +0 -0
  26. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/app/tests/__init__.py +0 -0
  27. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/app/tests/test_email_processing.py +0 -0
  28. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/app/tests/test_payment_profile_sorting.py +0 -0
  29. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/app/tests/test_spreadsheet_functions.py +0 -0
  30. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/aws/__init__.py +0 -0
  31. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/aws/amplify.py +0 -0
  32. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/aws/handlers/__init__.py +0 -0
  33. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/aws/handlers/exceptions.py +0 -0
  34. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/aws/handlers/response.py +0 -0
  35. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/aws/tests/__init__.py +0 -0
  36. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
  37. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/aws/tests/test_response.py +0 -0
  38. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/__init__.py +0 -0
  39. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/core/__init__.py +0 -0
  40. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/core/column.py +0 -0
  41. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/core/database.py +0 -0
  42. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/core/decorators.py +0 -0
  43. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/core/engine.py +0 -0
  44. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/core/result.py +0 -0
  45. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/core/row.py +0 -0
  46. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/core/sequence.py +0 -0
  47. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/core/table.py +0 -0
  48. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/core/transaction.py +0 -0
  49. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/exceptions.py +0 -0
  50. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/__init__.py +0 -0
  51. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/base/__init__.py +0 -0
  52. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/base/initializer.py +0 -0
  53. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/base/operators.py +0 -0
  54. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/base/sql.py +0 -0
  55. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/base/types.py +0 -0
  56. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/mysql/__init__.py +0 -0
  57. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/mysql/operators.py +0 -0
  58. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/mysql/reserved.py +0 -0
  59. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/mysql/sql.py +0 -0
  60. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/mysql/types.py +0 -0
  61. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/postgres/__init__.py +0 -0
  62. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/postgres/operators.py +0 -0
  63. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/postgres/reserved.py +0 -0
  64. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/postgres/sql.py +0 -0
  65. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/postgres/types.py +0 -0
  66. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/sqlite/__init__.py +0 -0
  67. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/sqlite/operators.py +0 -0
  68. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/sqlite/reserved.py +0 -0
  69. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/sqlite/sql.py +0 -0
  70. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/sqlite/types.py +0 -0
  71. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
  72. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/sqlserver/operators.py +0 -0
  73. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
  74. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/sqlserver/sql.py +0 -0
  75. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/sqlserver/types.py +0 -0
  76. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/servers/tablehelper.py +0 -0
  77. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/__init__.py +0 -0
  78. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/common_db_test.py +0 -0
  79. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/postgres/__init__.py +0 -0
  80. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/postgres/common.py +0 -0
  81. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/postgres/test_column.py +0 -0
  82. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/postgres/test_connections.py +0 -0
  83. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/postgres/test_database.py +0 -0
  84. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/postgres/test_engine.py +0 -0
  85. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
  86. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/postgres/test_imports.py +0 -0
  87. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/postgres/test_result.py +0 -0
  88. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/postgres/test_row.py +0 -0
  89. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
  90. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
  91. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
  92. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
  93. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
  94. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/postgres/test_table.py +0 -0
  95. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
  96. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
  97. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/sql/__init__.py +0 -0
  98. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/sql/common.py +0 -0
  99. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
  100. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
  101. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
  102. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/test_db_utils.py +0 -0
  103. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/test_postgres.py +0 -0
  104. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
  105. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
  106. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/test_result_caching.py +0 -0
  107. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
  108. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
  109. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
  110. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
  111. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/test_sql_builder.py +0 -0
  112. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/tests/test_tablehelper.py +0 -0
  113. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/db/utils.py +0 -0
  114. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/logging.py +0 -0
  115. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/misc/__init__.py +0 -0
  116. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/misc/conv/__init__.py +0 -0
  117. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/misc/conv/iconv.py +0 -0
  118. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/misc/conv/oconv.py +0 -0
  119. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/misc/db.py +0 -0
  120. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/misc/export.py +0 -0
  121. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/misc/format.py +0 -0
  122. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/misc/mail.py +0 -0
  123. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/misc/merge.py +0 -0
  124. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/misc/tests/__init__.py +0 -0
  125. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/misc/tests/test_db.py +0 -0
  126. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/misc/tests/test_fix.py +0 -0
  127. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/misc/tests/test_format.py +0 -0
  128. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/misc/tests/test_iconv.py +0 -0
  129. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/misc/tests/test_merge.py +0 -0
  130. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/misc/tests/test_oconv.py +0 -0
  131. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/misc/tests/test_original_error.py +0 -0
  132. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/misc/tests/test_timer.py +0 -0
  133. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/misc/timer.py +0 -0
  134. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity/misc/tools.py +0 -0
  135. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity_python.egg-info/dependency_links.txt +0 -0
  136. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity_python.egg-info/requires.txt +0 -0
  137. {velocity_python-0.0.169 → velocity_python-0.0.171}/src/velocity_python.egg-info/top_level.txt +0 -0
  138. {velocity_python-0.0.169 → velocity_python-0.0.171}/tests/test_decorators.py +0 -0
  139. {velocity_python-0.0.169 → velocity_python-0.0.171}/tests/test_lambda_handler.py +0 -0
  140. {velocity_python-0.0.169 → velocity_python-0.0.171}/tests/test_sys_modified_count_postgres_demo.py +0 -0
  141. {velocity_python-0.0.169 → velocity_python-0.0.171}/tests/test_table_alter.py +0 -0
  142. {velocity_python-0.0.169 → velocity_python-0.0.171}/tests/test_where_clause_validation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.0.169
3
+ Version: 0.0.171
4
4
  Summary: A rapid application development library for interfacing with data storage
5
5
  Author-email: Velocity Team <info@codeclubs.org>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "velocity-python"
7
- version = "0.0.169"
7
+ version = "0.0.171"
8
8
  authors = [
9
9
  { name="Velocity Team", email="info@codeclubs.org" },
10
10
  ]
@@ -1,4 +1,4 @@
1
- __version__ = version = "0.0.169"
1
+ __version__ = version = "0.0.171"
2
2
 
3
3
  from . import aws
4
4
  from . import db
@@ -5,7 +5,6 @@ This module provides a base class for handling AWS Lambda events.
5
5
  It includes common functionality shared between LambdaHandler and SqsHandler.
6
6
  """
7
7
 
8
- import logging
9
8
  import os
10
9
  import sys
11
10
  import traceback
@@ -146,10 +145,14 @@ class BaseHandler:
146
145
  if hasattr(self, action):
147
146
  result = getattr(self, action)(tx, local_context)
148
147
  if result is not None:
149
- logging.warning(
150
- f"Action {action} returned an unexpected non-None result ({result!r})."
151
- " Handlers should return None so results are not ignored."
152
- " Please refactor the action to avoid returning a value that will be discarded."
148
+ self.logger.warning(
149
+ "Action %s returned an unexpected non-None result (%r).",
150
+ action,
151
+ result,
152
+ extra={
153
+ "note": "Handlers should return None so results are not ignored.",
154
+ "action": action,
155
+ },
153
156
  )
154
157
  action_executed = True
155
158
  break
@@ -180,31 +183,25 @@ class BaseHandler:
180
183
  # Re-raise if no error handler is defined
181
184
  raise exception
182
185
 
183
- def log(self, tx, message: str, function: Optional[str] = None):
184
- """
185
- Log a message to the system log table.
186
- This is a base implementation that should be overridden by subclasses
187
- to provide handler-specific logging details.
188
-
189
- Args:
190
- tx: Database transaction object
191
- message: The message to log
192
- function: Optional function name, auto-detected if not provided
193
- """
186
+ def log(self, tx, message: str, function: Optional[str] = None, level: str = "info"):
187
+ """Emit structured log output through the shared logger."""
194
188
  if not function:
195
189
  function = self.get_calling_function()
196
190
 
197
- # Base log data - subclasses should extend this
198
- data = {
191
+ log_data = {
199
192
  "app_name": os.environ.get("ProjectName", "Unknown"),
200
193
  "function": function,
201
194
  "message": message,
202
195
  }
203
-
204
- # Let subclasses add their specific fields
205
- self._extend_log_data(data)
206
-
207
- tx.table("sys_log").insert(data)
196
+ self._extend_log_data(log_data)
197
+
198
+ log_method = getattr(self.logger, level, self.logger.info)
199
+ log_method(
200
+ "%s %s",
201
+ function,
202
+ message,
203
+ extra={"log_data": log_data, "tx_present": tx is not None},
204
+ )
208
205
 
209
206
  def _extend_log_data(self, data: Dict[str, Any]):
210
207
  """
@@ -1,5 +1,4 @@
1
1
  import json
2
- import logging
3
2
  import os
4
3
  import re
5
4
  import boto3
@@ -12,12 +11,13 @@ from botocore.exceptions import ClientError
12
11
  import hashlib
13
12
  import velocity
14
13
  import velocity.db
14
+ from velocity.logging import get_logger
15
15
 
16
16
  engine = velocity.db.postgres.initialize()
17
17
 
18
18
 
19
19
  cognito_client = boto3.client("cognito-idp")
20
- logger = logging.getLogger(__name__)
20
+ logger = get_logger("velocity.aws.handlers.context")
21
21
 
22
22
 
23
23
  @engine.transaction
@@ -83,11 +83,10 @@ class Context:
83
83
  def log(self, message, function=None):
84
84
  if self.__log:
85
85
  return self.__log(message, function)
86
+ if function:
87
+ logger.info("%s: %s", function, message)
86
88
  else:
87
- if function:
88
- print(f"{function}: {message}")
89
- else:
90
- print(f"{message}")
89
+ logger.info("%s", message)
91
90
 
92
91
  def update_job(self, tx, data=None):
93
92
  """Update job status and message in aws_job_activity table.
@@ -14,11 +14,6 @@ configure_logging()
14
14
  logger = get_logger("velocity.aws.handlers.lambda")
15
15
 
16
16
 
17
- def log_debug(*args) -> None:
18
- if args:
19
- logger.debug(" ".join(str(arg) for arg in args))
20
-
21
-
22
17
  class LambdaHandler(BaseHandler):
23
18
  def __init__(
24
19
  self,
@@ -59,143 +54,91 @@ class LambdaHandler(BaseHandler):
59
54
  else:
60
55
  self.session["device_type"] = "unknown"
61
56
 
62
- def get_pass_through_vars(self, tx, context):
63
- """Get pass-through variables: environment vars, layers, and user data"""
64
- temp = context.get_pass_through_vars(tx, self.current_user)
65
-
66
- log_debug(
67
- "LAYER_DEBUG: vars object contains:",
68
- list(temp.get("controls", {}).get("vars", {}).keys()),
69
- )
70
- if "layers" in temp.get("controls", {}).get("vars", {}):
71
- log_debug("LAYER_DEBUG: layers value:", temp["controls"]["vars"]["layers"])
72
-
73
- # Merge with user data from database
74
- temp = context.merge_user_data(tx, temp, self.session["email_address"])
75
-
76
- return temp
77
-
78
- def _summarize_postdata(self, postdata):
79
- """Extract key fields from postdata for logging without storing entire payload"""
80
- if not postdata:
81
- return {}
82
-
83
- summary = {
84
- "action": postdata.get("action"),
85
- "payload_keys": (
86
- list(postdata.get("payload", {}).keys())
87
- if isinstance(postdata.get("payload"), dict)
88
- else None
89
- ),
90
- }
91
-
92
- # Add size info
93
- try:
94
- summary["payload_size_bytes"] = len(json.dumps(postdata.get("payload", {})))
95
- except:
96
- summary["payload_size_bytes"] = 0
97
-
98
- return summary
99
57
 
100
58
  def beforeAction(self, tx, context):
101
59
  # Enhanced activity tracking
102
- result = self._enhanced_before_action(tx, context)
103
- # You can prevent further processing by returning True
104
- # All other return values are ignored
105
- if result is True:
60
+ stop_processing = self._enhanced_before_action(tx, context)
61
+ if stop_processing is True:
106
62
  return
107
- log_debug("starting BackOfficeEvents.beforeAction")
63
+ logger.debug("starting LamdaHandler.beforeAction")
108
64
 
109
- postdata = context.postdata()
110
- payload = context.payload()
111
- args = context.args()
112
65
  self.start = time.time()
113
66
  self.cognito_user = context.get_cognito_user(self.aws_event)
114
- self.current_user = {} # user data from database
115
-
116
- log_debug("DEBUG: !!! cognito_user", self.cognito_user)
117
- # Legacy activity tracking (keep for compatibility) - store summary not full postdata
118
- data = {}
119
- data["action"] = context.action()
120
- data["args"] = json.dumps(args)
121
- # Store postdata summary instead of full payload to avoid bloat
122
- data["postdata"] = json.dumps(self._summarize_postdata(postdata))
67
+ self.current_user = {}
68
+
69
+ logger.debug("DEBUG: !!! cognito_user %s", self.cognito_user)
123
70
  try:
124
71
  email_address = self.cognito_user["attributes"]["email"]
125
72
  self.session["email_address"] = email_address
126
- data["email_address"] = email_address
127
- except:
128
- pass
129
- data.update(self.session)
130
- self.logkey = tx.table("aws_api_activity").new(data).pk
73
+ except Exception:
74
+ self.logger.warning("Unable to read email from Cognito user", exc_info=True)
75
+
76
+ self.logger.info(
77
+ "Starting action",
78
+ extra={
79
+ "action": context.action(),
80
+ "context_args": context.args(),
81
+ "summary": self._summarize_postdata(context.postdata()),
82
+ "session_email": self.session.get("email_address"),
83
+ },
84
+ )
131
85
 
132
86
  row = tx.table(user_table).find(
133
- {"email_address": self.session["email_address"]}
87
+ {"email_address": self.session.get("email_address")}
134
88
  )
135
89
  if not row:
136
90
  raise Exception(
137
91
  "A valid user with permission is required to access this function [DB]"
138
92
  )
139
93
  self.current_user = row.to_dict()
140
- temp = copy.deepcopy(postdata)
94
+ temp = copy.deepcopy(context.postdata())
141
95
  temp["payload"].pop("cognito_user", None)
142
- self.log(tx, pprint.pformat(temp), f"Events.OnAction {temp['action']}")
96
+ self.logger.debug(
97
+ "Events.OnAction %s",
98
+ temp.get("action"),
99
+ extra={"payload": pprint.pformat(temp)},
100
+ )
143
101
 
144
102
  def afterAction(self, tx, context):
145
- # Enhanced activity tracking
146
-
147
-
148
- result = self._enhanced_after_action(tx, context)
149
- # You can prevent further processing by returning True
150
- # All other return values are ignored
151
- if result is True:
103
+ stop_processing = self._enhanced_after_action(tx, context)
104
+ if stop_processing is True:
152
105
  return
153
-
154
- log_debug("starting BackOfficeEvents.afterAction")
155
106
 
156
- # Legacy tracking (keep for compatibility)
107
+ logger.debug("starting LamdaHandler.afterAction")
108
+
157
109
  self.end = time.time()
158
- if hasattr(self, "logkey") and self.logkey:
159
- tx.table("aws_api_activity").update(
160
- {"duration": self.end - self.start}, self.logkey
161
- )
110
+ self.logger.info(
111
+ "Completed action",
112
+ extra={
113
+ "action": context.action(),
114
+ "duration": self.end - self.start,
115
+ },
116
+ )
162
117
 
163
118
  def onError(self, tx, context, exc, tb):
164
-
165
- result = self._enhanced_error_handler(error_tx, context, exc, tb)
166
- # You can prevent further processing by returning True
167
- # All other return values are ignored
168
- if result is True:
119
+ stop_processing = self._enhanced_error_handler(tx, context, exc, tb)
120
+ if stop_processing is True:
169
121
  return
170
- log_debug("starting BackOfficeEvents.aws_api_activity")
171
- with engine.transaction() as error_tx:
172
- is_production = os.environ.get("USER_BRANCH", "").lower() == "production"
173
- request_id = getattr(self.aws_context, "aws_request_id", None)
174
- message = f"Unhandled exception for action {context.action()} (request_id={request_id}): {exc}"
175
- if is_production:
176
- logger.error(message)
177
- else:
178
- logger.error("%s\n%s", message, tb)
179
-
180
- # Enhanced error handling
181
- log_debug("starting BackOfficeEvents.onError")
182
-
183
-
184
- # Legacy error handling (keep for compatibility)
185
- if hasattr(self, "logkey") and self.logkey:
186
- error_tx.table("aws_api_activity").update(
187
- {"exception": exc.__class__.__name__, "traceback": tb}, self.logkey
188
- )
189
- log_debug("starting BackOfficeEvents.copypayload")
190
- temp = copy.deepcopy(context.postdata())
191
- log_debug("starting BackOfficeEvents.log")
192
- temp["payload"].pop("cognito_user", None)
193
- self.log(
194
- error_tx,
195
- f"{pprint.pformat(temp)}\n\n{tb}\n\n{exc}",
196
- f"Events.OnError {temp['action']}",
197
- )
198
- log_debug("Ending BackOfficeEvents.OnError Done")
122
+ logger.debug("starting LamdaHandler.aws_api_activity")
123
+ request_id = getattr(self.aws_context, "aws_request_id", None)
124
+ self.logger.error(
125
+ "Unhandled exception for action %s",
126
+ context.action(),
127
+ exc_info=exc,
128
+ extra={"request_id": request_id},
129
+ )
130
+
131
+ logger.debug("starting BackOfficeEvents.onError")
132
+
133
+ temp = copy.deepcopy(context.postdata())
134
+ logger.debug("starting BackOfficeEvents.log")
135
+ temp["payload"].pop("cognito_user", None)
136
+ self.logger.error(
137
+ "Events.OnError %s",
138
+ temp.get("action"),
139
+ extra={"payload": pprint.pformat(temp), "traceback": tb},
140
+ )
141
+ logger.debug("Ending BackOfficeEvents.OnError Done")
199
142
 
200
143
  def serve(self, tx):
201
144
  response = Response()
@@ -223,7 +166,7 @@ class LambdaHandler(BaseHandler):
223
166
  postdata=postdata,
224
167
  response=response,
225
168
  session=self.session,
226
- log=lambda message, function=None: self.log(message, function),
169
+ log=lambda message, function=None: self.log(tx, message, function),
227
170
  )
228
171
 
229
172
  # Determine action from postdata or query parameters
@@ -268,11 +211,69 @@ class LambdaHandler(BaseHandler):
268
211
  except Exception as exc: # pragma: no cover - best effort logging
269
212
  self.log(tx, f"Failed to write tracking record: {exc}", "track")
270
213
 
214
+ def validate(self, user_required=True):
215
+ if user_required:
216
+ try:
217
+ attrs = self.cognito_user["attributes"]
218
+ assert attrs
219
+ assert attrs["email"]
220
+ assert attrs["sub"]
221
+ assert self.session["sub"]
222
+ assert self.session["sub"] == attrs["sub"]
223
+ except:
224
+ raise Exception("User Authentication Error [Cognito]")
225
+ else:
226
+ try:
227
+ if not self.session["sub"]:
228
+ # User is not signed in. If user_required
229
+ # is false, then simply return
230
+ return
231
+ attrs = self.cognito_user["attributes"]
232
+ assert attrs
233
+ assert attrs["email"]
234
+ assert attrs["sub"]
235
+ assert self.session["sub"]
236
+ assert self.session["sub"] == attrs["sub"]
237
+ except:
238
+ raise Exception("User Authentication Error [Cognito]")
239
+
240
+ def get_pass_through_vars(self, tx, context):
241
+ """Get pass-through variables: environment vars, layers, and user data"""
242
+ temp = context.get_pass_through_vars(tx, self.current_user)
271
243
 
272
- def OnActionTracking(self, tx, context):
273
- self.track(
274
- tx,
275
- context.payload().get("data", {}),
276
- user=context.session(),
277
- context_obj=context,
244
+ logger.debug(
245
+ "LAYER_DEBUG: vars object contains: %s",
246
+ list(temp.get("controls", {}).get("vars", {}).keys()),
278
247
  )
248
+ if "layers" in temp.get("controls", {}).get("vars", {}):
249
+ logger.debug(
250
+ "LAYER_DEBUG: layers value: %s",
251
+ temp["controls"]["vars"]["layers"],
252
+ )
253
+
254
+ # Merge with user data from database
255
+ temp = context.merge_user_data(tx, temp, self.session["email_address"])
256
+
257
+ return temp
258
+
259
+ def _summarize_postdata(self, postdata):
260
+ """Extract key fields from postdata for logging without storing entire payload"""
261
+ if not postdata:
262
+ return {}
263
+
264
+ summary = {
265
+ "action": postdata.get("action"),
266
+ "payload_keys": (
267
+ list(postdata.get("payload", {}).keys())
268
+ if isinstance(postdata.get("payload"), dict)
269
+ else None
270
+ ),
271
+ }
272
+
273
+ # Add size info
274
+ try:
275
+ summary["payload_size_bytes"] = len(json.dumps(postdata.get("payload", {})))
276
+ except:
277
+ summary["payload_size_bytes"] = 0
278
+
279
+ return summary
@@ -0,0 +1,10 @@
1
+ """
2
+ Mixins for AWS Lambda handlers.
3
+
4
+ This package exposes a single WebHandler mixin that unifies activity tracking,
5
+ logging, and error handling helpers for Lambda handlers.
6
+ """
7
+
8
+ from .web_handler import WebHandler
9
+
10
+ __all__ = ['WebHandler']