velocity-python 0.1.22__tar.gz → 0.1.24__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.
- {velocity_python-0.1.22/src/velocity_python.egg-info → velocity_python-0.1.24}/PKG-INFO +1 -1
- {velocity_python-0.1.22 → velocity_python-0.1.24}/pyproject.toml +1 -1
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/__init__.py +1 -1
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/amplify_build.py +62 -31
- velocity_python-0.1.24/src/velocity/aws/ssm_config.py +94 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/core/async_support.py +10 -2
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/core/transaction.py +16 -2
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/postgres/__init__.py +14 -13
- {velocity_python-0.1.22 → velocity_python-0.1.24/src/velocity_python.egg-info}/PKG-INFO +1 -1
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity_python.egg-info/SOURCES.txt +1 -0
- velocity_python-0.1.24/tests/test_amplify_build.py +150 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_async_support.py +5 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_observability.py +3 -0
- velocity_python-0.1.22/tests/test_amplify_build.py +0 -70
- {velocity_python-0.1.22 → velocity_python-0.1.24}/LICENSE +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/README.md +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/setup.cfg +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/__init__.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/amplify.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/handlers/__init__.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/handlers/base_handler.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/handlers/context.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/handlers/context_factory.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/handlers/exceptions.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/handlers/lambda_handler.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/handlers/mixins/data_service.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/handlers/mixins/web_handler.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/handlers/perf.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/handlers/response.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/handlers/sqs_handler.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/s3.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/tests/__init__.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/tests/test_base_handler_error_response.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/tests/test_response.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/__init__.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/core/__init__.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/core/column.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/core/database.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/core/decorators.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/core/engine.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/core/result.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/core/row.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/core/sequence.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/core/table.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/core/view.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/exceptions.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/migrations.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/__init__.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/base/__init__.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/base/initializer.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/base/operators.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/base/sql.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/base/types.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/mysql/__init__.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/mysql/operators.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/mysql/reserved.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/mysql/sql.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/mysql/types.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/postgres/operators.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/postgres/reserved.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/postgres/sql.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/postgres/types.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/sqlite/__init__.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/sqlite/operators.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/sqlite/reserved.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/sqlite/sql.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/sqlite/types.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/sqlserver/operators.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/sqlserver/sql.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/sqlserver/types.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/tablehelper.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/__init__.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/common_db_test.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/postgres/__init__.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/postgres/common.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/postgres/test_column.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/postgres/test_connections.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/postgres/test_database.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/postgres/test_engine.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/postgres/test_imports.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/postgres/test_result.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/postgres/test_row.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/postgres/test_table.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/sql/__init__.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/sql/common.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/test_db_utils.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/test_postgres.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/test_result_caching.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/test_sql_builder.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/test_tablehelper.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/tests/test_view_helper.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/utils.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/logging.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/misc/__init__.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/misc/conv/__init__.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/misc/conv/iconv.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/misc/conv/oconv.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/misc/db.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/misc/export.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/misc/format.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/misc/mail.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/misc/merge.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/misc/pdf.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/misc/tests/__init__.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/misc/tests/test_db.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/misc/tests/test_fix.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/misc/tests/test_format.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/misc/tests/test_iconv.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/misc/tests/test_merge.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/misc/tests/test_oconv.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/misc/tests/test_original_error.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/misc/tests/test_timer.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/misc/timer.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/misc/tools.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/payment/__init__.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/payment/authorizenet_adapter.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/payment/base_adapter.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/payment/braintree_adapter.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/payment/charge_rules.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/payment/stripe_adapter.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity_python.egg-info/dependency_links.txt +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity_python.egg-info/entry_points.txt +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity_python.egg-info/requires.txt +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity_python.egg-info/top_level.txt +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_batch_operations.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_concurrency_safety.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_connection_pool.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_connection_resilience.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_decorators.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_email_processing.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_iconv_money_to_cents.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_lambda_handler.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_lambda_handler_auth.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_mixins_import.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_n_plus_one.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_payment_braintree_adapter.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_payment_profile_sorting.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_payment_stripe_adapter.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_pdf.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_prepared_statements.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_psycopg3_upgrade.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_query_cache.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_row_batch_update.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_row_cache_staleness.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_row_dirty_tracking.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_schema_migrations.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_security_hardening.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_spreadsheet_functions.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_sqs_per_record_transactions.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_sys_modified_count_postgres_demo.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_table_alter.py +0 -0
- {velocity_python-0.1.22 → velocity_python-0.1.24}/tests/test_where_clause_validation.py +0 -0
|
@@ -235,6 +235,36 @@ def retryable_call(
|
|
|
235
235
|
raise
|
|
236
236
|
|
|
237
237
|
|
|
238
|
+
def resolve_latest_layer_arns(
|
|
239
|
+
lambda_client,
|
|
240
|
+
current_layers: Sequence[Mapping[str, Any]],
|
|
241
|
+
) -> Sequence[str]:
|
|
242
|
+
updated_layer_arns = []
|
|
243
|
+
|
|
244
|
+
for layer in current_layers or ():
|
|
245
|
+
layer_arn = layer["Arn"]
|
|
246
|
+
layer_name = layer_arn.split(":")[-2]
|
|
247
|
+
current_version = layer_arn.split(":")[-1]
|
|
248
|
+
|
|
249
|
+
latest_response = retryable_call(
|
|
250
|
+
lambda_client.list_layer_versions,
|
|
251
|
+
LayerName=layer_name,
|
|
252
|
+
MaxItems=1,
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
if latest_response and latest_response.get("LayerVersions"):
|
|
256
|
+
latest_version_info = latest_response["LayerVersions"][0]
|
|
257
|
+
latest_version = latest_version_info["Version"]
|
|
258
|
+
latest_arn = latest_version_info["LayerVersionArn"]
|
|
259
|
+
updated_layer_arns.append(
|
|
260
|
+
latest_arn if str(latest_version) != str(current_version) else layer_arn
|
|
261
|
+
)
|
|
262
|
+
else:
|
|
263
|
+
updated_layer_arns.append(layer_arn)
|
|
264
|
+
|
|
265
|
+
return updated_layer_arns
|
|
266
|
+
|
|
267
|
+
|
|
238
268
|
def _serialize_policy_document(policy_document: Any) -> str:
|
|
239
269
|
if isinstance(policy_document, str):
|
|
240
270
|
return policy_document
|
|
@@ -443,6 +473,16 @@ def run_backend_deployment(
|
|
|
443
473
|
|
|
444
474
|
timeout_value = config.resolve_timeout(function_name)
|
|
445
475
|
memory_value = config.resolve_memory_size(function_name)
|
|
476
|
+
layers_value = None
|
|
477
|
+
current_layers = function.get("Layers") or []
|
|
478
|
+
if current_layers:
|
|
479
|
+
try:
|
|
480
|
+
layers_value = list(resolve_latest_layer_arns(lambda_client, current_layers))
|
|
481
|
+
except Exception as exc:
|
|
482
|
+
print(
|
|
483
|
+
f"[WARN] Unable to resolve latest layers for {function_name}: {exc}. "
|
|
484
|
+
"Keeping current layer attachments."
|
|
485
|
+
)
|
|
446
486
|
print(
|
|
447
487
|
f"[INFO] Updating Lambda function {function_name} with timeout={timeout_value}"
|
|
448
488
|
+ (
|
|
@@ -456,6 +496,7 @@ def run_backend_deployment(
|
|
|
456
496
|
memory_size=memory_value,
|
|
457
497
|
subnet_ids=subnet_ids,
|
|
458
498
|
security_group_ids=security_group_ids,
|
|
499
|
+
layers=layers_value,
|
|
459
500
|
)
|
|
460
501
|
|
|
461
502
|
if not set_cloudwatch_logging(
|
|
@@ -563,44 +604,34 @@ def update_lambda_layers_to_latest(
|
|
|
563
604
|
layer_arn = layer["Arn"]
|
|
564
605
|
layer_name = layer_arn.split(":")[-2]
|
|
565
606
|
current_version = layer_arn.split(":")[-1]
|
|
566
|
-
|
|
567
607
|
print(
|
|
568
608
|
f"[INFO] Processing layer: {layer_name} (current version: {current_version})"
|
|
569
609
|
)
|
|
570
610
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
611
|
+
try:
|
|
612
|
+
updated_layer_arns = list(
|
|
613
|
+
resolve_latest_layer_arns(lambda_client, current_layers)
|
|
614
|
+
)
|
|
615
|
+
except Exception as exc:
|
|
616
|
+
print(f"[ERROR] Failed to resolve latest layers: {exc}")
|
|
617
|
+
print("[INFO] Keeping current layer versions")
|
|
618
|
+
updated_layer_arns = [layer["Arn"] for layer in current_layers]
|
|
619
|
+
|
|
620
|
+
for original_arn, updated_arn in zip(
|
|
621
|
+
[layer["Arn"] for layer in current_layers],
|
|
622
|
+
updated_layer_arns,
|
|
623
|
+
):
|
|
624
|
+
layer_name = original_arn.split(":")[-2]
|
|
625
|
+
current_version = original_arn.split(":")[-1]
|
|
626
|
+
latest_version = updated_arn.split(":")[-1]
|
|
627
|
+
if updated_arn != original_arn:
|
|
628
|
+
print(
|
|
629
|
+
f"[INFO] Updating {layer_name}: v{current_version} -> v{latest_version}"
|
|
576
630
|
)
|
|
577
|
-
|
|
578
|
-
if latest_response and latest_response.get("LayerVersions"):
|
|
579
|
-
latest_version_info = latest_response["LayerVersions"][0]
|
|
580
|
-
latest_version = latest_version_info["Version"]
|
|
581
|
-
latest_arn = latest_version_info["LayerVersionArn"]
|
|
582
|
-
|
|
583
|
-
if str(latest_version) != str(current_version):
|
|
584
|
-
print(
|
|
585
|
-
f"[INFO] Updating {layer_name}: v{current_version} -> v{latest_version}"
|
|
586
|
-
)
|
|
587
|
-
updated_layer_arns.append(latest_arn)
|
|
588
|
-
else:
|
|
589
|
-
print(
|
|
590
|
-
f"[INFO] {layer_name} is already at latest version (v{latest_version})"
|
|
591
|
-
)
|
|
592
|
-
updated_layer_arns.append(layer_arn)
|
|
593
|
-
else:
|
|
594
|
-
print(
|
|
595
|
-
f"[WARN] No versions found for layer {layer_name}, keeping current version"
|
|
596
|
-
)
|
|
597
|
-
updated_layer_arns.append(layer_arn)
|
|
598
|
-
except Exception as exc:
|
|
631
|
+
else:
|
|
599
632
|
print(
|
|
600
|
-
f"[
|
|
633
|
+
f"[INFO] {layer_name} is already at latest version (v{latest_version})"
|
|
601
634
|
)
|
|
602
|
-
print(f"[INFO] Keeping current version for {layer_name}")
|
|
603
|
-
updated_layer_arns.append(layer_arn)
|
|
604
635
|
|
|
605
636
|
current_layer_arns = [layer["Arn"] for layer in current_layers]
|
|
606
637
|
if updated_layer_arns != current_layer_arns:
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SSM-backed configuration with environment-variable fallback.
|
|
3
|
+
|
|
4
|
+
Opt-in: set ``VELOCITY_SSM_ENABLED=true`` in your Amplify environment variables
|
|
5
|
+
(pushed via ``bootstrap.py secrets``).
|
|
6
|
+
|
|
7
|
+
When enabled, ``getenv(key)`` reads from SSM at::
|
|
8
|
+
|
|
9
|
+
/{ProjectName}/{ENV}/{key}
|
|
10
|
+
|
|
11
|
+
and falls back to ``os.environ`` on a miss or any error. Results are cached
|
|
12
|
+
for the process lifetime so SSM is called at most once per key per Lambda
|
|
13
|
+
cold start.
|
|
14
|
+
|
|
15
|
+
When **not** enabled (the default), ``getenv`` is a thin wrapper around
|
|
16
|
+
``os.environ.get`` with zero extra overhead — preserving full backward
|
|
17
|
+
compatibility for projects that don't use SSM (e.g. caringcent).
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import logging
|
|
23
|
+
import os
|
|
24
|
+
from typing import Optional
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
# Per-process cache: key → value (str) or sentinel _MISS.
|
|
29
|
+
_MISS = object()
|
|
30
|
+
_cache: dict[str, object] = {}
|
|
31
|
+
|
|
32
|
+
# Lazily evaluated once per process.
|
|
33
|
+
_SSM_ENABLED: bool | None = None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _is_enabled() -> bool:
|
|
37
|
+
global _SSM_ENABLED
|
|
38
|
+
if _SSM_ENABLED is None:
|
|
39
|
+
_SSM_ENABLED = os.environ.get('VELOCITY_SSM_ENABLED', '').lower() in ('1', 'true', 'yes')
|
|
40
|
+
return _SSM_ENABLED
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _ssm_prefix() -> str | None:
|
|
44
|
+
"""Return '/{ProjectName}/{ENV}' or None if either var is missing."""
|
|
45
|
+
project = os.environ.get('ProjectName')
|
|
46
|
+
env = os.environ.get('ENV')
|
|
47
|
+
if project and env:
|
|
48
|
+
return f'/{project}/{env}'
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _fetch(key: str) -> str | None:
|
|
53
|
+
"""Single SSM GetParameter call; returns value or None on any failure."""
|
|
54
|
+
prefix = _ssm_prefix()
|
|
55
|
+
if prefix is None:
|
|
56
|
+
return None
|
|
57
|
+
param_name = f'{prefix}/{key}'
|
|
58
|
+
try:
|
|
59
|
+
import boto3
|
|
60
|
+
ssm = boto3.client('ssm', region_name=os.environ.get('REGION', 'us-east-1'))
|
|
61
|
+
resp = ssm.get_parameter(Name=param_name, WithDecryption=True)
|
|
62
|
+
return resp['Parameter']['Value']
|
|
63
|
+
except Exception as exc:
|
|
64
|
+
logger.debug('SSM miss for %s: %s', param_name, exc)
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def getenv(key: str, default: Optional[str] = None) -> Optional[str]:
|
|
69
|
+
"""
|
|
70
|
+
Read a config value, checking SSM before ``os.environ``.
|
|
71
|
+
|
|
72
|
+
SSM is only consulted when ``VELOCITY_SSM_ENABLED=true`` is set in the
|
|
73
|
+
Lambda environment *and* both ``ProjectName`` and ``ENV`` are present.
|
|
74
|
+
All other cases fall straight through to ``os.environ``.
|
|
75
|
+
|
|
76
|
+
Results are cached per process so SSM is queried at most once per key
|
|
77
|
+
per Lambda cold start.
|
|
78
|
+
"""
|
|
79
|
+
if _is_enabled():
|
|
80
|
+
if key not in _cache:
|
|
81
|
+
value = _fetch(key)
|
|
82
|
+
_cache[key] = value if value is not None else _MISS
|
|
83
|
+
cached = _cache[key]
|
|
84
|
+
if cached is not _MISS:
|
|
85
|
+
return cached # type: ignore[return-value]
|
|
86
|
+
|
|
87
|
+
return os.environ.get(key, default)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def clear_cache() -> None:
|
|
91
|
+
"""Clear the SSM cache (useful in tests)."""
|
|
92
|
+
_cache.clear()
|
|
93
|
+
global _SSM_ENABLED
|
|
94
|
+
_SSM_ENABLED = None
|
|
@@ -493,10 +493,18 @@ class AsyncTransaction:
|
|
|
493
493
|
|
|
494
494
|
# R12 — Slow query logging
|
|
495
495
|
if _SLOW_QUERY_MS and elapsed_ms > _SLOW_QUERY_MS:
|
|
496
|
+
from velocity.db.core.transaction import _summarize_sql
|
|
497
|
+
|
|
498
|
+
sql_preview = _summarize_sql(sql)
|
|
496
499
|
_logger.warning(
|
|
497
500
|
"Slow async query: %.1f ms sql=%s",
|
|
498
|
-
elapsed_ms,
|
|
499
|
-
|
|
501
|
+
elapsed_ms,
|
|
502
|
+
sql_preview,
|
|
503
|
+
extra={
|
|
504
|
+
"query_duration_ms": round(elapsed_ms, 1),
|
|
505
|
+
"sql_preview": sql_preview,
|
|
506
|
+
},
|
|
507
|
+
stack_info=True,
|
|
500
508
|
)
|
|
501
509
|
|
|
502
510
|
# R14 — N+1 detection
|
|
@@ -23,6 +23,7 @@ _DEFAULT_QUERY_CACHE_SIZE = int(os.environ.get("VELOCITY_QUERY_CACHE_SIZE", "100
|
|
|
23
23
|
|
|
24
24
|
# Slow-query threshold in milliseconds (0 = disabled).
|
|
25
25
|
_SLOW_QUERY_MS = int(os.environ.get("VELOCITY_SLOW_QUERY_MS", "500"))
|
|
26
|
+
_SLOW_QUERY_SQL_CHARS = int(os.environ.get("VELOCITY_SLOW_QUERY_SQL_CHARS", "4000"))
|
|
26
27
|
|
|
27
28
|
# N+1 detection: warn when the same table is SELECTed more than this many
|
|
28
29
|
# times within a single transaction. 0 = disabled. Only active when the
|
|
@@ -76,6 +77,16 @@ def _extract_table_name(sql):
|
|
|
76
77
|
return None
|
|
77
78
|
|
|
78
79
|
|
|
80
|
+
def _summarize_sql(sql, limit=None):
|
|
81
|
+
if not sql:
|
|
82
|
+
return None
|
|
83
|
+
compact = " ".join(str(sql).split())
|
|
84
|
+
max_chars = _SLOW_QUERY_SQL_CHARS if limit is None else limit
|
|
85
|
+
if max_chars and len(compact) > max_chars:
|
|
86
|
+
return compact[:max_chars] + "... [truncated]"
|
|
87
|
+
return compact
|
|
88
|
+
|
|
89
|
+
|
|
79
90
|
class Transaction:
|
|
80
91
|
"""
|
|
81
92
|
Encapsulates a single transaction in the database (connection + commit/rollback).
|
|
@@ -210,14 +221,17 @@ class Transaction:
|
|
|
210
221
|
if _SLOW_QUERY_MS and elapsed_ms > _SLOW_QUERY_MS:
|
|
211
222
|
op = _classify_sql(sql)
|
|
212
223
|
tbl = _extract_table_name(sql)
|
|
224
|
+
sql_preview = _summarize_sql(sql)
|
|
213
225
|
_logger.warning(
|
|
214
|
-
"Slow query (%s): %.1f ms table=%s",
|
|
215
|
-
op, elapsed_ms, tbl,
|
|
226
|
+
"Slow query (%s): %.1f ms table=%s sql=%s",
|
|
227
|
+
op, elapsed_ms, tbl, sql_preview,
|
|
216
228
|
extra={
|
|
217
229
|
"query_duration_ms": round(elapsed_ms, 1),
|
|
218
230
|
"table_name": tbl,
|
|
219
231
|
"operation": op,
|
|
232
|
+
"sql_preview": sql_preview,
|
|
220
233
|
},
|
|
234
|
+
stack_info=True,
|
|
221
235
|
)
|
|
222
236
|
|
|
223
237
|
# R14 — N+1 detection (only when debug=True).
|
{velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/db/servers/postgres/__init__.py
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from ..base.initializer import BaseInitializer
|
|
3
3
|
from velocity.db.core import engine
|
|
4
|
+
from velocity.aws.ssm_config import getenv as _getenv
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
# Default TCP keepalive settings for PostgreSQL connections.
|
|
@@ -41,14 +42,14 @@ class PostgreSQLInitializer(BaseInitializer):
|
|
|
41
42
|
|
|
42
43
|
from .sql import SQL
|
|
43
44
|
|
|
44
|
-
# Base configuration from environment
|
|
45
|
+
# Base configuration from environment / SSM
|
|
45
46
|
# psycopg v3 uses libpq param names; 'dbname' not 'database'.
|
|
46
47
|
base_config = {
|
|
47
|
-
"dbname":
|
|
48
|
-
"host":
|
|
49
|
-
"port":
|
|
50
|
-
"user":
|
|
51
|
-
"password":
|
|
48
|
+
"dbname": _getenv("DBDatabase"),
|
|
49
|
+
"host": _getenv("DBHost"),
|
|
50
|
+
"port": _getenv("DBPort"),
|
|
51
|
+
"user": _getenv("DBUser"),
|
|
52
|
+
"password": _getenv("DBPassword"),
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
# Remove None values
|
|
@@ -65,7 +66,7 @@ class PostgreSQLInitializer(BaseInitializer):
|
|
|
65
66
|
|
|
66
67
|
# SSL mode — default to "prefer" so connections upgrade when the
|
|
67
68
|
# server supports TLS but don't fail when it doesn't.
|
|
68
|
-
ssl_mode =
|
|
69
|
+
ssl_mode = _getenv("VELOCITY_SSL_MODE")
|
|
69
70
|
if ssl_mode:
|
|
70
71
|
final_config.setdefault("sslmode", ssl_mode)
|
|
71
72
|
|
|
@@ -108,11 +109,11 @@ def initialize(config=None, schema_locked=False, **kwargs):
|
|
|
108
109
|
from .sql import SQL
|
|
109
110
|
|
|
110
111
|
konfig = {
|
|
111
|
-
"dbname": os.environ["DBDatabase"],
|
|
112
|
-
"host": os.environ["DBHost"],
|
|
113
|
-
"port": os.environ["DBPort"],
|
|
114
|
-
"user": os.environ["DBUser"],
|
|
115
|
-
"password": os.environ["DBPassword"],
|
|
112
|
+
"dbname": _getenv("DBDatabase") or os.environ["DBDatabase"],
|
|
113
|
+
"host": _getenv("DBHost") or os.environ["DBHost"],
|
|
114
|
+
"port": _getenv("DBPort") or os.environ["DBPort"],
|
|
115
|
+
"user": _getenv("DBUser") or os.environ["DBUser"],
|
|
116
|
+
"password": _getenv("DBPassword") or os.environ["DBPassword"],
|
|
116
117
|
}
|
|
117
118
|
konfig.update(config or {})
|
|
118
119
|
konfig.update(kwargs)
|
|
@@ -122,7 +123,7 @@ def initialize(config=None, schema_locked=False, **kwargs):
|
|
|
122
123
|
konfig.setdefault(key, default_val)
|
|
123
124
|
|
|
124
125
|
# SSL mode.
|
|
125
|
-
ssl_mode =
|
|
126
|
+
ssl_mode = _getenv("VELOCITY_SSL_MODE")
|
|
126
127
|
if ssl_mode:
|
|
127
128
|
konfig.setdefault("sslmode", ssl_mode)
|
|
128
129
|
|
|
@@ -7,6 +7,7 @@ src/velocity/aws/__init__.py
|
|
|
7
7
|
src/velocity/aws/amplify.py
|
|
8
8
|
src/velocity/aws/amplify_build.py
|
|
9
9
|
src/velocity/aws/s3.py
|
|
10
|
+
src/velocity/aws/ssm_config.py
|
|
10
11
|
src/velocity/aws/handlers/__init__.py
|
|
11
12
|
src/velocity/aws/handlers/base_handler.py
|
|
12
13
|
src/velocity/aws/handlers/context.py
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
from velocity.aws.amplify_build import (
|
|
2
|
+
BackendDeploymentConfig,
|
|
3
|
+
build_environment_variables,
|
|
4
|
+
get_amplify_app_id_and_branch,
|
|
5
|
+
run_backend_deployment,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FakeAmplifyProject:
|
|
10
|
+
def get_merged_env_vars(self, branch):
|
|
11
|
+
return {"EXISTING": branch}
|
|
12
|
+
|
|
13
|
+
def get_region(self):
|
|
14
|
+
return "us-east-1"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FakeDeployProject(FakeAmplifyProject):
|
|
18
|
+
def __init__(self):
|
|
19
|
+
self.updated_functions = []
|
|
20
|
+
|
|
21
|
+
def list_lambda_functions_filtered(self, branch):
|
|
22
|
+
return [
|
|
23
|
+
{
|
|
24
|
+
"FunctionName": f"ClientEvents-{branch}",
|
|
25
|
+
"Layers": [
|
|
26
|
+
{
|
|
27
|
+
"Arn": "arn:aws:lambda:us-east-1:741671896925:layer:py-lib-support:225"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"Arn": "arn:aws:lambda:us-east-1:741671896925:layer:py-lib-accounting:34"
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
def update_lambda_function(self, **kwargs):
|
|
37
|
+
self.updated_functions.append(kwargs)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class FakeLambdaClient:
|
|
41
|
+
def list_layer_versions(self, LayerName, MaxItems):
|
|
42
|
+
versions = {
|
|
43
|
+
"py-lib-support": [
|
|
44
|
+
{
|
|
45
|
+
"Version": 240,
|
|
46
|
+
"LayerVersionArn": "arn:aws:lambda:us-east-1:741671896925:layer:py-lib-support:240",
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"py-lib-accounting": [
|
|
50
|
+
{
|
|
51
|
+
"Version": 34,
|
|
52
|
+
"LayerVersionArn": "arn:aws:lambda:us-east-1:741671896925:layer:py-lib-accounting:34",
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
}
|
|
56
|
+
return {"LayerVersions": versions[LayerName][:MaxItems]}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_backend_deployment_config_resolves_queue_timeout_and_memory_size():
|
|
60
|
+
config = BackendDeploymentConfig(
|
|
61
|
+
queue_name_template="clients-queue-{branch}",
|
|
62
|
+
short_timeout_tokens=("Events", "Data"),
|
|
63
|
+
memory_size_by_token={"QueueHandler": 1024, "Events": 512},
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
assert config.resolve_queue_name("Demo") == "clients-queue-demo"
|
|
67
|
+
assert config.resolve_timeout("ClientEvents-demo") == 60
|
|
68
|
+
assert config.resolve_timeout("ClientUtility-demo") == 900
|
|
69
|
+
assert config.resolve_memory_size("ClientQueueHandler-demo") == 1024
|
|
70
|
+
assert config.resolve_memory_size("ClientEvents-demo") == 512
|
|
71
|
+
assert config.resolve_memory_size("ClientUtility-demo") is None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_build_environment_variables_reads_project_metadata(tmp_path, monkeypatch):
|
|
75
|
+
amplify_dir = tmp_path / "amplify" / "backend"
|
|
76
|
+
amplify_dir.mkdir(parents=True)
|
|
77
|
+
(amplify_dir / "amplify-meta.json").write_text('{"UserPoolId": "us-east-1_demoPool",}')
|
|
78
|
+
|
|
79
|
+
monkeypatch.setenv("AWS_JOB_ID", "job-123")
|
|
80
|
+
app = FakeAmplifyProject()
|
|
81
|
+
|
|
82
|
+
env_vars = build_environment_variables(
|
|
83
|
+
app,
|
|
84
|
+
"demo",
|
|
85
|
+
"clients-queue-demo",
|
|
86
|
+
project_root=tmp_path,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
assert env_vars["EXISTING"] == "demo"
|
|
90
|
+
assert env_vars["SqsWorkQueue"] == "clients-queue-demo"
|
|
91
|
+
assert env_vars["AWS_JOB_ID"] == "job-123"
|
|
92
|
+
assert env_vars["AWS_USER_POOL_ID"] == "us-east-1_demoPool"
|
|
93
|
+
assert env_vars["USER_BRANCH"] == "demo"
|
|
94
|
+
assert env_vars["REACT_APP_USER_BRANCH"] == "demo"
|
|
95
|
+
assert env_vars["USER_REGION"] == "us-east-1"
|
|
96
|
+
assert env_vars["LOGLEVEL"] == "INFO"
|
|
97
|
+
assert "BUILD_DATETIME" in env_vars
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_get_amplify_app_id_and_branch_uses_team_provider_info(tmp_path, monkeypatch):
|
|
101
|
+
team_provider_path = tmp_path / "amplify"
|
|
102
|
+
team_provider_path.mkdir(parents=True)
|
|
103
|
+
(team_provider_path / "team-provider-info.json").write_text(
|
|
104
|
+
'{"demo": {"awscloudformation": {"AmplifyAppId": "app-123"}}}'
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
monkeypatch.setenv("AWS_BRANCH", "demo")
|
|
108
|
+
monkeypatch.delenv("AWS_APP_ID", raising=False)
|
|
109
|
+
|
|
110
|
+
app_id, branch = get_amplify_app_id_and_branch(tmp_path)
|
|
111
|
+
|
|
112
|
+
assert app_id == "app-123"
|
|
113
|
+
assert branch == "demo"
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def test_run_backend_deployment_refreshes_lambda_layers(monkeypatch, tmp_path):
|
|
117
|
+
monkeypatch.setattr(
|
|
118
|
+
"velocity.aws.amplify_build.ensure_lambda_policies_and_attach",
|
|
119
|
+
lambda *args, **kwargs: None,
|
|
120
|
+
)
|
|
121
|
+
monkeypatch.setattr(
|
|
122
|
+
"velocity.aws.amplify_build.set_cloudwatch_logging",
|
|
123
|
+
lambda *args, **kwargs: True,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
app = FakeDeployProject()
|
|
127
|
+
config = BackendDeploymentConfig(
|
|
128
|
+
queue_name_template="clients-queue-{branch}",
|
|
129
|
+
short_timeout_tokens=("Events",),
|
|
130
|
+
memory_size_by_token={"Events": 512},
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
run_backend_deployment(
|
|
134
|
+
"app-123",
|
|
135
|
+
"demo",
|
|
136
|
+
config,
|
|
137
|
+
project_root=tmp_path,
|
|
138
|
+
app=app,
|
|
139
|
+
lambda_client=FakeLambdaClient(),
|
|
140
|
+
logs_client=object(),
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
assert len(app.updated_functions) == 1
|
|
144
|
+
assert app.updated_functions[0]["function_name"] == "ClientEvents-demo"
|
|
145
|
+
assert app.updated_functions[0]["timeout"] == 60
|
|
146
|
+
assert app.updated_functions[0]["memory_size"] == 512
|
|
147
|
+
assert app.updated_functions[0]["layers"] == [
|
|
148
|
+
"arn:aws:lambda:us-east-1:741671896925:layer:py-lib-support:240",
|
|
149
|
+
"arn:aws:lambda:us-east-1:741671896925:layer:py-lib-accounting:34",
|
|
150
|
+
]
|
|
@@ -687,3 +687,8 @@ class TestAsyncSlowQueryLogging:
|
|
|
687
687
|
if "Slow" in str(c)
|
|
688
688
|
]
|
|
689
689
|
assert len(slow_calls) >= 1
|
|
690
|
+
args = slow_calls[0].args
|
|
691
|
+
kwargs = slow_calls[0].kwargs
|
|
692
|
+
assert args[2] == "SELECT * FROM large_table"
|
|
693
|
+
assert kwargs["extra"]["sql_preview"] == "SELECT * FROM large_table"
|
|
694
|
+
assert kwargs["stack_info"] is True
|
|
@@ -177,6 +177,7 @@ class TestSlowQueryLogging:
|
|
|
177
177
|
|
|
178
178
|
assert any("Slow query" in r.message for r in caplog.records)
|
|
179
179
|
assert any("big_table" in r.message for r in caplog.records)
|
|
180
|
+
assert any("sql=SELECT * FROM big_table" in r.message for r in caplog.records)
|
|
180
181
|
|
|
181
182
|
def test_fast_query_no_warning(self, caplog):
|
|
182
183
|
tx = _make_tx()
|
|
@@ -230,6 +231,8 @@ class TestSlowQueryLogging:
|
|
|
230
231
|
assert rec.query_duration_ms > 0
|
|
231
232
|
assert rec.table_name == "orders"
|
|
232
233
|
assert rec.operation == "SELECT"
|
|
234
|
+
assert rec.sql_preview == "SELECT * FROM orders WHERE id = 1"
|
|
235
|
+
assert rec.stack_info
|
|
233
236
|
|
|
234
237
|
|
|
235
238
|
# ──────────────────────────────────────────────────────────────────────
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
from velocity.aws.amplify_build import (
|
|
2
|
-
BackendDeploymentConfig,
|
|
3
|
-
build_environment_variables,
|
|
4
|
-
get_amplify_app_id_and_branch,
|
|
5
|
-
)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class FakeAmplifyProject:
|
|
9
|
-
def get_merged_env_vars(self, branch):
|
|
10
|
-
return {"EXISTING": branch}
|
|
11
|
-
|
|
12
|
-
def get_region(self):
|
|
13
|
-
return "us-east-1"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def test_backend_deployment_config_resolves_queue_timeout_and_memory_size():
|
|
17
|
-
config = BackendDeploymentConfig(
|
|
18
|
-
queue_name_template="clients-queue-{branch}",
|
|
19
|
-
short_timeout_tokens=("Events", "Data"),
|
|
20
|
-
memory_size_by_token={"QueueHandler": 1024, "Events": 512},
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
assert config.resolve_queue_name("Demo") == "clients-queue-demo"
|
|
24
|
-
assert config.resolve_timeout("ClientEvents-demo") == 60
|
|
25
|
-
assert config.resolve_timeout("ClientUtility-demo") == 900
|
|
26
|
-
assert config.resolve_memory_size("ClientQueueHandler-demo") == 1024
|
|
27
|
-
assert config.resolve_memory_size("ClientEvents-demo") == 512
|
|
28
|
-
assert config.resolve_memory_size("ClientUtility-demo") is None
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def test_build_environment_variables_reads_project_metadata(tmp_path, monkeypatch):
|
|
32
|
-
amplify_dir = tmp_path / "amplify" / "backend"
|
|
33
|
-
amplify_dir.mkdir(parents=True)
|
|
34
|
-
(amplify_dir / "amplify-meta.json").write_text('{"UserPoolId": "us-east-1_demoPool",}')
|
|
35
|
-
|
|
36
|
-
monkeypatch.setenv("AWS_JOB_ID", "job-123")
|
|
37
|
-
app = FakeAmplifyProject()
|
|
38
|
-
|
|
39
|
-
env_vars = build_environment_variables(
|
|
40
|
-
app,
|
|
41
|
-
"demo",
|
|
42
|
-
"clients-queue-demo",
|
|
43
|
-
project_root=tmp_path,
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
assert env_vars["EXISTING"] == "demo"
|
|
47
|
-
assert env_vars["SqsWorkQueue"] == "clients-queue-demo"
|
|
48
|
-
assert env_vars["AWS_JOB_ID"] == "job-123"
|
|
49
|
-
assert env_vars["AWS_USER_POOL_ID"] == "us-east-1_demoPool"
|
|
50
|
-
assert env_vars["USER_BRANCH"] == "demo"
|
|
51
|
-
assert env_vars["REACT_APP_USER_BRANCH"] == "demo"
|
|
52
|
-
assert env_vars["USER_REGION"] == "us-east-1"
|
|
53
|
-
assert env_vars["LOGLEVEL"] == "INFO"
|
|
54
|
-
assert "BUILD_DATETIME" in env_vars
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def test_get_amplify_app_id_and_branch_uses_team_provider_info(tmp_path, monkeypatch):
|
|
58
|
-
team_provider_path = tmp_path / "amplify"
|
|
59
|
-
team_provider_path.mkdir(parents=True)
|
|
60
|
-
(team_provider_path / "team-provider-info.json").write_text(
|
|
61
|
-
'{"demo": {"awscloudformation": {"AmplifyAppId": "app-123"}}}'
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
monkeypatch.setenv("AWS_BRANCH", "demo")
|
|
65
|
-
monkeypatch.delenv("AWS_APP_ID", raising=False)
|
|
66
|
-
|
|
67
|
-
app_id, branch = get_amplify_app_id_and_branch(tmp_path)
|
|
68
|
-
|
|
69
|
-
assert app_id == "app-123"
|
|
70
|
-
assert branch == "demo"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/handlers/context_factory.py
RENAMED
|
File without changes
|
|
File without changes
|
{velocity_python-0.1.22 → velocity_python-0.1.24}/src/velocity/aws/handlers/lambda_handler.py
RENAMED
|
File without changes
|