velocity-python 0.1.127__tar.gz → 0.1.129__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.127 → velocity_python-0.1.129}/PKG-INFO +2 -4
- {velocity_python-0.1.127 → velocity_python-0.1.129}/pyproject.toml +2 -5
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/__init__.py +1 -1
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/amplify.py +16 -25
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/amplify_build.py +13 -31
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/capabilities/jobs.py +23 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/deploy/__init__.py +12 -0
- velocity_python-0.1.129/src/velocity/aws/deploy/config.py +115 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/deploy/operations.py +188 -39
- velocity_python-0.1.129/src/velocity/aws/deploy/policies.py +563 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/deploy/reconcile.py +125 -52
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/facets.py +61 -13
- velocity_python-0.1.129/src/velocity/aws/failures.py +502 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/handlers/base_handler.py +59 -1
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/handlers/context.py +148 -84
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/handlers/exceptions.py +8 -1
- velocity_python-0.1.129/src/velocity/aws/handlers/mixins/client_input.py +481 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/handlers/mixins/data_service.py +88 -15
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/handlers/mixins/job_handler.py +55 -5
- velocity_python-0.1.129/src/velocity/aws/handlers/sqs_handler.py +248 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/s3.py +55 -15
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/tests/test_amplify_build_shim.py +9 -2
- velocity_python-0.1.129/src/velocity/aws/tests/test_deploy_least_privilege.py +449 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/tests/test_deploy_policies.py +58 -3
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/tests/test_deploy_reconcile.py +30 -46
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/cache.py +39 -2
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/__init__.py +3 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/core/decorators.py +138 -23
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/core/engine.py +149 -22
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/core/result.py +4 -1
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/core/row.py +45 -12
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/core/table.py +105 -39
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/core/transaction.py +401 -94
- velocity_python-0.1.129/src/velocity/db/fragments.py +225 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/postgres/sql.py +397 -147
- velocity_python-0.1.129/src/velocity/db/servers/tablehelper.py +1213 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/pdf.py +20 -148
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/payment/__init__.py +8 -1
- velocity_python-0.1.129/src/velocity/payment/_mirror_core.py +107 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/payment/authorizenet_adapter.py +548 -406
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/payment/authorizenet_mirror.py +20 -22
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/payment/base_adapter.py +289 -6
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/payment/braintree_adapter.py +408 -300
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/payment/braintree_mirror.py +17 -37
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/payment/charge_rules.py +29 -14
- velocity_python-0.1.129/src/velocity/payment/money.py +163 -0
- velocity_python-0.1.129/src/velocity/payment/redact.py +76 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/payment/stripe_adapter.py +710 -362
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/payment/stripe_mirror.py +32 -43
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity_python.egg-info/PKG-INFO +2 -4
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity_python.egg-info/SOURCES.txt +17 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity_python.egg-info/requires.txt +0 -3
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_amplify_build.py +2 -6
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_batch_operations.py +11 -12
- velocity_python-0.1.129/tests/test_client_sql_boundary.py +1071 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_concurrency_safety.py +12 -3
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_facets.py +23 -19
- velocity_python-0.1.129/tests/test_failures.py +295 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_job_handler.py +113 -13
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_job_lifecycle.py +17 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_on_commit.py +84 -8
- velocity_python-0.1.129/tests/test_payment_audit_fixes.py +1048 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_payment_authorizenet_adapter.py +2 -2
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_payment_braintree_mirror.py +11 -31
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_payment_mirror_empty_table.py +26 -32
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_payment_stripe_adapter.py +41 -27
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_pdf.py +68 -175
- velocity_python-0.1.129/tests/test_pgserver_bulk_cache.py +147 -0
- velocity_python-0.1.129/tests/test_pgserver_pointer_joins.py +238 -0
- velocity_python-0.1.129/tests/test_pointer_join_sql.py +342 -0
- velocity_python-0.1.129/tests/test_raw_sql_prefix_policy.py +282 -0
- velocity_python-0.1.129/tests/test_s3_list_client_files.py +99 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_sqs_per_record_transactions.py +94 -22
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_table_find_cache.py +26 -1
- velocity_python-0.1.129/tests/test_tx_lifecycle_retry.py +496 -0
- velocity_python-0.1.129/tests/test_tx_read_cache_and_bulk.py +358 -0
- velocity_python-0.1.127/src/velocity/aws/deploy/config.py +0 -65
- velocity_python-0.1.127/src/velocity/aws/deploy/policies.py +0 -311
- velocity_python-0.1.127/src/velocity/aws/handlers/sqs_handler.py +0 -151
- velocity_python-0.1.127/src/velocity/db/servers/tablehelper.py +0 -692
- {velocity_python-0.1.127 → velocity_python-0.1.129}/LICENSE +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/README.md +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/setup.cfg +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/assets/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/assets/backfill.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/assets/indexing.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/assets/references.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/assets/service.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/assets/usage_index.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/blobs.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/capabilities/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/capabilities/base.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/capabilities/dispatch.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/capabilities/infra.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/capabilities/provisioning.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/capabilities/retention.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/capabilities/security.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/capabilities/single_session.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/deploy/targets.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/dirty_pipeline.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/email.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/feature_flags.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/handlers/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/handlers/context_factory.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/handlers/lambda_handler.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/handlers/masquerade.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/handlers/mixins/session_control.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/handlers/mixins/web_handler.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/handlers/perf.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/handlers/response.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/job_lifecycle.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/job_results.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/mirror.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/ops.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/sms.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/ssm_config.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/tests/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/tests/test_amplify_build_policy_size.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/tests/test_base_handler_error_response.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/tests/test_email.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/tests/test_response.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/aws/tests/test_sms.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/core/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/core/async_support.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/core/column.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/core/database.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/core/jsonproxy.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/core/lazy_engine.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/core/sequence.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/core/view.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/exceptions.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/maintenance.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/migrations.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/base/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/base/initializer.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/base/operators.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/base/sql.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/base/types.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/mysql/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/mysql/operators.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/mysql/reserved.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/mysql/sql.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/mysql/types.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/postgres/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/postgres/operators.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/postgres/reserved.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/postgres/types.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/sqlite/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/sqlite/operators.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/sqlite/reserved.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/sqlite/sql.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/sqlite/types.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/sqlserver/operators.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/sqlserver/sql.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/servers/sqlserver/types.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/common_db_test.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/common.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/conftest.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_column.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_connections.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_database.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_engine.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_imports.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_result.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_row.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_table.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/sql/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/sql/common.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/test_db_utils.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/test_postgres.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/test_result_caching.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/test_sql_builder.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/test_tablehelper.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tests/test_view_helper.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/tunnel.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/db/utils.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/logging.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/conv/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/conv/iconv.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/conv/oconv.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/db.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/export.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/format.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/mail.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/merge.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/tests/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/tests/test_db.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/tests/test_fix.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/tests/test_format.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/tests/test_iconv.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/tests/test_mail_send.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/tests/test_merge.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/tests/test_oconv.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/tests/test_original_error.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/tests/test_timer.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/timer.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/misc/tools.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/nosql/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/nosql/core/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/nosql/core/capabilities.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/nosql/core/store.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/nosql/core/where.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/nosql/exceptions.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/nosql/servers/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/nosql/servers/base/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/nosql/servers/cassandra/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/nosql/servers/cassandra/translation.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/nosql/servers/couchbase/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/nosql/servers/couchbase/translation.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/nosql/servers/dynamodb/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/nosql/servers/dynamodb/translation.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/nosql/servers/elasticsearch/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/nosql/servers/elasticsearch/translation.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/nosql/servers/memory/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/nosql/servers/mongodb/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/nosql/servers/mongodb/translation.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity/nosql/servers/redis/__init__.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity_python.egg-info/dependency_links.txt +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity_python.egg-info/entry_points.txt +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/src/velocity_python.egg-info/top_level.txt +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_access_error_403.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_activity_tracking_aborted_tx.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_asset_indexing.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_asset_references.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_assets_service.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_async_support.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_blob_store.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_capabilities.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_capability_catalog.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_cognito_user_cache.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_column_tx_arg.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_connection_pool.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_connection_resilience.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_context_flags_loader.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_context_job_descriptions.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_data_service_query_cap.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_data_service_renumber.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_data_service_save_as.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_db_credentials_ssm_cascade.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_db_maintenance.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_db_tunnel.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_decorators.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_delete_hook_fk_blocked.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_dirty_pipeline_fast_path.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_email_processing.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_enqueue_metric_namespace.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_enqueue_send_failures.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_feature_flags.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_get_cognito_user_provider.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_http_handler_rollback.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_iconv_money_to_cents.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_identifier_injection_guard.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_job_results.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_json_columns.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_jsonb_dict_adapter.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_lambda_handler.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_lambda_handler_auth.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_lambda_handler_masquerade.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_lambda_handler_user_cache.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_lazy_engine.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_masquerade_grant.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_mirror.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_mixins_import.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_n_plus_one.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_nosql_dynamodb.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_nosql_memory.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_nosql_mongodb.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_nosql_redis.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_nosql_translation.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_nosql_where.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_observability.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_ops.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_payment_braintree_adapter.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_payment_profile_sorting.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_prepared_statements.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_psycopg3_upgrade.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_query_cache.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_query_missing_table_unlocked.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_read_cache.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_response.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_restricted_direct_tables.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_result_returning.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_retry_side_effect_guard.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_return_default_safety.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_row_batch_update.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_row_cache_staleness.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_s3_bucket_config.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_schema_migrations.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_security_capability.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_security_hardening.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_server_cursor.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_session_control.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_single_autocommit_safety.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_single_session_capability.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_spreadsheet_functions.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_sqlite_backend.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_ssm_config.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_store_user_data.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_sys_modified_count_postgres_demo.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_table_alter.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_table_purge.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_transaction_class_wrapping.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_transaction_commit_and_ownership.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_transaction_edge_cases.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_where_clause_validation.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_write_hook_create_flow.py +0 -0
- {velocity_python-0.1.127 → velocity_python-0.1.129}/tests/test_write_hook_empty_strings.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: velocity-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.129
|
|
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
|
|
@@ -34,9 +34,7 @@ Requires-Dist: odfpy>=1.4.0; extra == "ods"
|
|
|
34
34
|
Provides-Extra: templates
|
|
35
35
|
Requires-Dist: jinja2>=3.1.0; extra == "templates"
|
|
36
36
|
Provides-Extra: pdf
|
|
37
|
-
Requires-Dist:
|
|
38
|
-
Provides-Extra: pdf-weasyprint
|
|
39
|
-
Requires-Dist: weasyprint>=60.0; extra == "pdf-weasyprint"
|
|
37
|
+
Requires-Dist: weasyprint>=60.0; extra == "pdf"
|
|
40
38
|
Provides-Extra: http
|
|
41
39
|
Requires-Dist: requests>=2.32.0; extra == "http"
|
|
42
40
|
Provides-Extra: mysql
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "velocity-python"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.129"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name="Velocity Team", email="info@codeclubs.org" },
|
|
10
10
|
]
|
|
@@ -54,11 +54,8 @@ ods = [
|
|
|
54
54
|
templates = [
|
|
55
55
|
"jinja2>=3.1.0",
|
|
56
56
|
]
|
|
57
|
+
# HTML→PDF via WeasyPrint; native libs (Pango/Cairo) come from the OS or a layer.
|
|
57
58
|
pdf = [
|
|
58
|
-
"pdfkit>=1.0.0",
|
|
59
|
-
]
|
|
60
|
-
# Maintained HTML→PDF engine; native libs (Pango/Cairo) come from the OS or a layer.
|
|
61
|
-
pdf-weasyprint = [
|
|
62
59
|
"weasyprint>=60.0",
|
|
63
60
|
]
|
|
64
61
|
http = [
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import boto3
|
|
2
|
-
import time
|
|
3
2
|
from collections.abc import Mapping, Sequence
|
|
4
3
|
|
|
5
4
|
|
|
@@ -190,31 +189,23 @@ class AmplifyProject:
|
|
|
190
189
|
)
|
|
191
190
|
|
|
192
191
|
def check_policies(self, function):
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
192
|
+
"""Attach the shared ENI baseline (AWS-managed
|
|
193
|
+
AWSLambdaVPCAccessExecutionRole) to the function's role.
|
|
194
|
+
|
|
195
|
+
No longer attaches the account-wide ``lambda-vpc-execution`` policy or
|
|
196
|
+
AmazonSQSFullAccess (AWS-1); app grants come from
|
|
197
|
+
velocity.aws.deploy.run_deployment's per-app, per-stage policy.
|
|
198
|
+
"""
|
|
199
|
+
from velocity.aws.deploy.policies import (
|
|
200
|
+
SHARED_BASELINE_POLICY_ARNS,
|
|
201
|
+
attach_role_policies,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
attach_role_policies(
|
|
205
|
+
boto3.client("iam"),
|
|
206
|
+
function["Role"].split("/")[-1],
|
|
207
|
+
SHARED_BASELINE_POLICY_ARNS,
|
|
197
208
|
)
|
|
198
|
-
has_policy_attached = False
|
|
199
|
-
for policy in response["AttachedPolicies"]:
|
|
200
|
-
if policy["PolicyName"] == "lambda-vpc-execution":
|
|
201
|
-
has_policy_attached = True
|
|
202
|
-
if not has_policy_attached:
|
|
203
|
-
account_id = self.get_account_id()
|
|
204
|
-
iam_client.attach_role_policy(
|
|
205
|
-
PolicyArn=f"arn:aws:iam::{account_id}:policy/lambda-vpc-execution",
|
|
206
|
-
RoleName=function["Role"].split("/")[1],
|
|
207
|
-
)
|
|
208
|
-
time.sleep(15)
|
|
209
|
-
has_policy_attached = False
|
|
210
|
-
for policy in response["AttachedPolicies"]:
|
|
211
|
-
if policy["PolicyName"] == "AmazonSQSFullAccess":
|
|
212
|
-
has_policy_attached = True
|
|
213
|
-
if not has_policy_attached:
|
|
214
|
-
iam_client.attach_role_policy(
|
|
215
|
-
PolicyArn="arn:aws:iam::aws:policy/AmazonSQSFullAccess",
|
|
216
|
-
RoleName=function["Role"].split("/")[1],
|
|
217
|
-
)
|
|
218
209
|
|
|
219
210
|
def sync(self, branch):
|
|
220
211
|
raise RuntimeError(
|
|
@@ -5,9 +5,10 @@ package (config / targets / policies / operations / reconcile). This module
|
|
|
5
5
|
keeps every name and signature the existing project build.py files import,
|
|
6
6
|
with pre-refactor behavior:
|
|
7
7
|
|
|
8
|
-
- run_backend_deployment()
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
- run_backend_deployment() delegates to velocity.aws.deploy.run_deployment()
|
|
9
|
+
(per-app, per-stage declarative policies). The former "legacy-union" mode,
|
|
10
|
+
which unioned every app's extras into the shared lambda-vpc-execution
|
|
11
|
+
policy, was removed (AWS-1): no consumer used it.
|
|
11
12
|
|
|
12
13
|
New projects should import from velocity.aws.deploy directly.
|
|
13
14
|
"""
|
|
@@ -80,33 +81,15 @@ def ensure_lambda_policies_and_attach(
|
|
|
80
81
|
config: Optional[DeploymentConfig] = None,
|
|
81
82
|
iam_client=None,
|
|
82
83
|
) -> None:
|
|
83
|
-
"""
|
|
84
|
-
lambda-vpc-execution policy
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
policy_document = config.build_lambda_policy_document(policy_document)
|
|
93
|
-
|
|
94
|
-
try:
|
|
95
|
-
retryable_call(iam_client.get_policy, PolicyArn=policy_arn)
|
|
96
|
-
except iam_client.exceptions.NoSuchEntityException:
|
|
97
|
-
_ensure_policy_document_size("lambda-vpc-execution", policy_document)
|
|
98
|
-
print("Creating policy lambda-vpc-execution")
|
|
99
|
-
retryable_call(
|
|
100
|
-
iam_client.create_policy,
|
|
101
|
-
PolicyName="lambda-vpc-execution",
|
|
102
|
-
PolicyDocument=_serialize_policy_document(policy_document),
|
|
103
|
-
)
|
|
104
|
-
time.sleep(10)
|
|
105
|
-
else:
|
|
106
|
-
sync_managed_policy_document(iam_client, policy_arn, policy_document)
|
|
107
|
-
|
|
108
|
-
role_name = function["Role"].split("/")[-1]
|
|
109
|
-
attach_role_policies(iam_client, role_name, [policy_arn, AWS_SQS_FULL_ACCESS_ARN])
|
|
84
|
+
"""Removed: this unioned app grants into the account-wide
|
|
85
|
+
lambda-vpc-execution policy and attached AmazonSQSFullAccess (AWS-1).
|
|
86
|
+
Use velocity.aws.deploy.run_deployment(), which reconciles a per-app,
|
|
87
|
+
per-stage policy declaratively."""
|
|
88
|
+
del app, function, config, iam_client
|
|
89
|
+
raise RuntimeError(
|
|
90
|
+
"ensure_lambda_policies_and_attach was removed (it granted account-wide "
|
|
91
|
+
"SSM/Cognito/SQS access); use velocity.aws.deploy.run_deployment()"
|
|
92
|
+
)
|
|
110
93
|
|
|
111
94
|
|
|
112
95
|
def set_cloudwatch_logging(
|
|
@@ -141,7 +124,6 @@ def run_backend_deployment(
|
|
|
141
124
|
run_deployment(
|
|
142
125
|
target,
|
|
143
126
|
config,
|
|
144
|
-
policy_mode="legacy-union",
|
|
145
127
|
lambda_client=lambda_client,
|
|
146
128
|
logs_client=logs_client,
|
|
147
129
|
)
|
|
@@ -53,7 +53,30 @@ class Jobs(Capability):
|
|
|
53
53
|
where={"sys_id": job["sys_id"]},
|
|
54
54
|
)
|
|
55
55
|
expired += 1
|
|
56
|
+
_report_after_commit(tx, job, status)
|
|
56
57
|
|
|
57
58
|
days = int(config.get("job_retention_days") or 30)
|
|
58
59
|
purged = jobs.purge({"<sys_created": now - datetime.timedelta(days=days)})
|
|
59
60
|
return {"expired": expired, "purged": purged}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _report_after_commit(tx, job, status):
|
|
64
|
+
"""A job whose worker died never reached onError, so nothing reported it:
|
|
65
|
+
publish a ``source="job"`` failure event once the sweep commits."""
|
|
66
|
+
from velocity.aws import failures
|
|
67
|
+
|
|
68
|
+
job_id = job.get("job_id")
|
|
69
|
+
action = job.get("action")
|
|
70
|
+
|
|
71
|
+
def _send():
|
|
72
|
+
failures.report(
|
|
73
|
+
None,
|
|
74
|
+
failures.JobWorkerDied(
|
|
75
|
+
f"job {job_id} stuck in {status}: {STALE_FAILURE['error']}"
|
|
76
|
+
),
|
|
77
|
+
source="job",
|
|
78
|
+
action=action,
|
|
79
|
+
job_id=job_id,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
tx.on_commit(_send)
|
|
@@ -17,11 +17,15 @@ See docs/deploy.md in the velocity-python repo.
|
|
|
17
17
|
|
|
18
18
|
from velocity.aws.deploy.config import BackendDeploymentConfig, DeploymentConfig
|
|
19
19
|
from velocity.aws.deploy.policies import (
|
|
20
|
+
AWS_LAMBDA_VPC_ACCESS_ARN,
|
|
20
21
|
IAM_MANAGED_POLICY_MAX_CHARS,
|
|
21
22
|
ManagedPolicySizeError,
|
|
22
23
|
app_policy_name,
|
|
24
|
+
app_runtime_statements,
|
|
23
25
|
baseline_lambda_policy_document,
|
|
24
26
|
ensure_app_policy,
|
|
27
|
+
reconcile_role_policies,
|
|
28
|
+
retire_policy,
|
|
25
29
|
sync_managed_policy_document,
|
|
26
30
|
)
|
|
27
31
|
from velocity.aws.deploy.operations import (
|
|
@@ -29,7 +33,9 @@ from velocity.aws.deploy.operations import (
|
|
|
29
33
|
ensure_cloudwatch_log_group,
|
|
30
34
|
ensure_dlq,
|
|
31
35
|
ensure_identity_pool_developer_provider,
|
|
36
|
+
ensure_queue_event_source_mapping,
|
|
32
37
|
list_functions_by_tag,
|
|
38
|
+
queue_visibility_timeout,
|
|
33
39
|
resolve_latest_layer_arns,
|
|
34
40
|
retryable_call,
|
|
35
41
|
set_cloudwatch_logging,
|
|
@@ -48,6 +54,7 @@ from velocity.aws.deploy.reconcile import (
|
|
|
48
54
|
)
|
|
49
55
|
|
|
50
56
|
__all__ = [
|
|
57
|
+
"AWS_LAMBDA_VPC_ACCESS_ARN",
|
|
51
58
|
"AmplifyTarget",
|
|
52
59
|
"BackendDeploymentConfig",
|
|
53
60
|
"DeploymentConfig",
|
|
@@ -55,16 +62,21 @@ __all__ = [
|
|
|
55
62
|
"ManagedPolicySizeError",
|
|
56
63
|
"PlainTarget",
|
|
57
64
|
"app_policy_name",
|
|
65
|
+
"app_runtime_statements",
|
|
58
66
|
"baseline_lambda_policy_document",
|
|
59
67
|
"ensure_api_cors_header",
|
|
60
68
|
"ensure_app_policy",
|
|
61
69
|
"ensure_cloudwatch_log_group",
|
|
62
70
|
"ensure_dlq",
|
|
63
71
|
"ensure_identity_pool_developer_provider",
|
|
72
|
+
"ensure_queue_event_source_mapping",
|
|
64
73
|
"get_amplify_app_id_and_branch",
|
|
65
74
|
"initialize_build_environment",
|
|
66
75
|
"list_functions_by_tag",
|
|
76
|
+
"queue_visibility_timeout",
|
|
77
|
+
"reconcile_role_policies",
|
|
67
78
|
"resolve_latest_layer_arns",
|
|
79
|
+
"retire_policy",
|
|
68
80
|
"retryable_call",
|
|
69
81
|
"run_deployment",
|
|
70
82
|
"set_cloudwatch_logging",
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""Declarative deployment configuration for velocity.aws.deploy.
|
|
2
|
+
|
|
3
|
+
DeploymentConfig describes everything a project's backend deployment needs:
|
|
4
|
+
queue naming, VPC placement, per-function timeout/memory overrides, and the
|
|
5
|
+
project's IAM policy statements. It is platform-neutral — nothing here assumes
|
|
6
|
+
Amplify.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from typing import Any, Dict, Mapping, Optional, Sequence, Tuple
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class DeploymentConfig:
|
|
15
|
+
queue_name_template: str
|
|
16
|
+
subnet_ids: Tuple[str, ...] = field(default_factory=tuple)
|
|
17
|
+
security_group_ids: Tuple[str, ...] = field(default_factory=tuple)
|
|
18
|
+
short_timeout_seconds: int = 60
|
|
19
|
+
default_timeout_seconds: int = 900
|
|
20
|
+
short_timeout_tokens: Tuple[str, ...] = (
|
|
21
|
+
"Events",
|
|
22
|
+
"Data",
|
|
23
|
+
"Public",
|
|
24
|
+
"WebHooks",
|
|
25
|
+
)
|
|
26
|
+
memory_size_by_token: Mapping[str, int] = field(default_factory=dict)
|
|
27
|
+
extra_lambda_policy_statements: Tuple[Mapping[str, Any], ...] = field(
|
|
28
|
+
default_factory=tuple
|
|
29
|
+
)
|
|
30
|
+
use_reserved_concurrency: bool = True
|
|
31
|
+
|
|
32
|
+
# ── Per-app/stage generated IAM (see policies.app_runtime_statements) ──
|
|
33
|
+
# SSM read is scoped to /{ssm_project_name}/{stage}/* and /Global/{stage}/*.
|
|
34
|
+
# ssm_project_name is the runtime ProjectName (e.g. "Donor" for the donors
|
|
35
|
+
# app); None falls back to the deploy target's app name.
|
|
36
|
+
ssm_project_name: Optional[str] = None
|
|
37
|
+
ssm_read_global: bool = True
|
|
38
|
+
# Extra SSM read prefixes, "{stage}" substituted (e.g. "/Donor/{stage}").
|
|
39
|
+
extra_ssm_read_prefixes: Tuple[str, ...] = field(default_factory=tuple)
|
|
40
|
+
# Cognito user pools this app's functions call, per stage ("*" = every
|
|
41
|
+
# stage): pool ids ("us-east-1_AbC123") or user-pool ARNs.
|
|
42
|
+
cognito_user_pools: Mapping[str, Tuple[str, ...]] = field(default_factory=dict)
|
|
43
|
+
# Config keys resolved at deploy time to pool ids
|
|
44
|
+
# (/{ssm_project_name}/{stage}/{key}, then /Global/{stage}/{key}).
|
|
45
|
+
cognito_user_pool_config_keys: Tuple[str, ...] = field(default_factory=tuple)
|
|
46
|
+
# cognito-idp actions granted on those pools. The default is what
|
|
47
|
+
# velocity's own runtime needs (Context.get_cognito_user).
|
|
48
|
+
cognito_actions: Tuple[str, ...] = (
|
|
49
|
+
"cognito-idp:AdminGetUser",
|
|
50
|
+
"cognito-idp:ListUsers",
|
|
51
|
+
)
|
|
52
|
+
# Other queues this app sends to ("{branch}"/"{raw_branch}" substituted).
|
|
53
|
+
extra_send_queue_templates: Tuple[str, ...] = field(default_factory=tuple)
|
|
54
|
+
# SNS topic for velocity.aws.failures (the FailureTopicArn config key);
|
|
55
|
+
# "{stage}", "{region}", "{account}" substituted. Grants sns:Publish.
|
|
56
|
+
failure_topic_arn: Optional[str] = None
|
|
57
|
+
|
|
58
|
+
# ── Work queue (setup_sqs_queue_and_permissions) ──
|
|
59
|
+
# VisibilityTimeout = clamp(handler timeout x multiplier, 30 s, 12 h).
|
|
60
|
+
queue_visibility_timeout_multiplier: int = 6
|
|
61
|
+
queue_dlq_enabled: bool = True
|
|
62
|
+
queue_dlq_max_receive_count: int = 3
|
|
63
|
+
queue_batch_size: int = 1
|
|
64
|
+
|
|
65
|
+
def resolve_ssm_project_name(self, app_name: str) -> str:
|
|
66
|
+
return self.ssm_project_name or app_name
|
|
67
|
+
|
|
68
|
+
def resolve_cognito_user_pools(self, stage: str) -> Tuple[str, ...]:
|
|
69
|
+
pools = list(self.cognito_user_pools.get("*", ()))
|
|
70
|
+
for key in (stage, stage.lower()):
|
|
71
|
+
for pool in self.cognito_user_pools.get(key, ()):
|
|
72
|
+
if pool not in pools:
|
|
73
|
+
pools.append(pool)
|
|
74
|
+
return tuple(pools)
|
|
75
|
+
|
|
76
|
+
def resolve_send_queue_names(self, stage: str) -> Tuple[str, ...]:
|
|
77
|
+
return tuple(
|
|
78
|
+
template.format(branch=stage.lower(), raw_branch=stage)
|
|
79
|
+
for template in self.extra_send_queue_templates
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def resolve_queue_name(self, stage: str) -> str:
|
|
83
|
+
return self.queue_name_template.format(branch=stage.lower(), raw_branch=stage)
|
|
84
|
+
|
|
85
|
+
def resolve_timeout(self, function_name: str) -> int:
|
|
86
|
+
if any(token in function_name for token in self.short_timeout_tokens):
|
|
87
|
+
return self.short_timeout_seconds
|
|
88
|
+
return self.default_timeout_seconds
|
|
89
|
+
|
|
90
|
+
def resolve_memory_size(self, function_name: str) -> Optional[int]:
|
|
91
|
+
for token, memory_size in self.memory_size_by_token.items():
|
|
92
|
+
if token in function_name:
|
|
93
|
+
return memory_size
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
def build_lambda_policy_document(
|
|
97
|
+
self, base_policy_document: Mapping[str, Any]
|
|
98
|
+
) -> Dict[str, Any]:
|
|
99
|
+
"""Legacy helper: base document + this config's extra statements.
|
|
100
|
+
|
|
101
|
+
The shared-union policy mode it served was removed (no consumer used
|
|
102
|
+
it); kept for importers. Deployments read
|
|
103
|
+
extra_lambda_policy_statements directly into the per-app policy.
|
|
104
|
+
"""
|
|
105
|
+
policy_document = dict(base_policy_document)
|
|
106
|
+
statements = list(policy_document.get("Statement", []))
|
|
107
|
+
statements.extend(
|
|
108
|
+
dict(statement) for statement in self.extra_lambda_policy_statements
|
|
109
|
+
)
|
|
110
|
+
policy_document["Statement"] = statements
|
|
111
|
+
return policy_document
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
# Backwards-compatible name used by existing build.py files.
|
|
115
|
+
BackendDeploymentConfig = DeploymentConfig
|
|
@@ -376,6 +376,113 @@ def build_sqs_queue_policy(
|
|
|
376
376
|
)
|
|
377
377
|
|
|
378
378
|
|
|
379
|
+
MAX_VISIBILITY_TIMEOUT_SECONDS = 12 * 60 * 60
|
|
380
|
+
MIN_VISIBILITY_TIMEOUT_SECONDS = 30
|
|
381
|
+
REPORT_BATCH_ITEM_FAILURES = "ReportBatchItemFailures"
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def queue_visibility_timeout(
|
|
385
|
+
function_timeout: Optional[int], multiplier: int = 6
|
|
386
|
+
) -> int:
|
|
387
|
+
"""AWS guidance for an SQS event source: visibility timeout of at least
|
|
388
|
+
six times the function timeout, clamped to [30 s, 12 h]."""
|
|
389
|
+
seconds = int(function_timeout or 0) * int(multiplier)
|
|
390
|
+
return max(
|
|
391
|
+
MIN_VISIBILITY_TIMEOUT_SECONDS, min(seconds, MAX_VISIBILITY_TIMEOUT_SECONDS)
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def _queue_url_or_none(sqs_client, queue_name: str) -> Optional[str]:
|
|
396
|
+
try:
|
|
397
|
+
return sqs_client.get_queue_url(QueueName=queue_name)["QueueUrl"]
|
|
398
|
+
except sqs_client.exceptions.QueueDoesNotExist:
|
|
399
|
+
return None
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def _ensure_dlq_queue(sqs_client, dlq_name: str, retention_seconds: int) -> str:
|
|
403
|
+
"""Create ``dlq_name`` if missing; return its ARN."""
|
|
404
|
+
dlq_url = _queue_url_or_none(sqs_client, dlq_name)
|
|
405
|
+
if dlq_url is None:
|
|
406
|
+
dlq_url = sqs_client.create_queue(
|
|
407
|
+
QueueName=dlq_name,
|
|
408
|
+
Attributes={"MessageRetentionPeriod": str(retention_seconds)},
|
|
409
|
+
)["QueueUrl"]
|
|
410
|
+
print(f"[INFO] Created DLQ: {dlq_name}")
|
|
411
|
+
return sqs_client.get_queue_attributes(
|
|
412
|
+
QueueUrl=dlq_url, AttributeNames=["QueueArn"]
|
|
413
|
+
)["Attributes"]["QueueArn"]
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def _normalized_attribute(name: str, value: Optional[str]) -> Any:
|
|
417
|
+
if value is None:
|
|
418
|
+
return None
|
|
419
|
+
if name in ("Policy", "RedrivePolicy"):
|
|
420
|
+
try:
|
|
421
|
+
return json.loads(value)
|
|
422
|
+
except (TypeError, ValueError):
|
|
423
|
+
return value
|
|
424
|
+
return str(value)
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def ensure_queue_event_source_mapping(
|
|
428
|
+
lambda_client,
|
|
429
|
+
*,
|
|
430
|
+
queue_arn: str,
|
|
431
|
+
function_name: str,
|
|
432
|
+
batch_size: int = 1,
|
|
433
|
+
) -> dict:
|
|
434
|
+
"""Create or correct the SQS -> Lambda mapping.
|
|
435
|
+
|
|
436
|
+
The mapping must report partial batch failures
|
|
437
|
+
(``FunctionResponseTypes=["ReportBatchItemFailures"]``), otherwise the
|
|
438
|
+
handler's ``batchItemFailures`` response is ignored and failed records
|
|
439
|
+
are deleted as if they succeeded. Existing mappings are updated in place
|
|
440
|
+
(batch size and response types); ``Enabled`` is left as found. Prints and
|
|
441
|
+
returns the diff.
|
|
442
|
+
"""
|
|
443
|
+
mappings = []
|
|
444
|
+
paginator = lambda_client.get_paginator("list_event_source_mappings")
|
|
445
|
+
for page in paginator.paginate(
|
|
446
|
+
EventSourceArn=queue_arn, FunctionName=function_name
|
|
447
|
+
):
|
|
448
|
+
mappings.extend(page.get("EventSourceMappings", []))
|
|
449
|
+
|
|
450
|
+
if not mappings:
|
|
451
|
+
print(
|
|
452
|
+
f" + event source mapping {queue_arn.rsplit(':', 1)[-1]} -> {function_name} "
|
|
453
|
+
f"(BatchSize={batch_size}, FunctionResponseTypes=[{REPORT_BATCH_ITEM_FAILURES}])"
|
|
454
|
+
)
|
|
455
|
+
lambda_client.create_event_source_mapping(
|
|
456
|
+
EventSourceArn=queue_arn,
|
|
457
|
+
FunctionName=function_name,
|
|
458
|
+
Enabled=True,
|
|
459
|
+
BatchSize=batch_size,
|
|
460
|
+
FunctionResponseTypes=[REPORT_BATCH_ITEM_FAILURES],
|
|
461
|
+
)
|
|
462
|
+
return {"created": True, "updated": []}
|
|
463
|
+
|
|
464
|
+
updated = []
|
|
465
|
+
for mapping in mappings:
|
|
466
|
+
changes = {}
|
|
467
|
+
response_types = list(mapping.get("FunctionResponseTypes") or [])
|
|
468
|
+
if REPORT_BATCH_ITEM_FAILURES not in response_types:
|
|
469
|
+
changes["FunctionResponseTypes"] = response_types + [
|
|
470
|
+
REPORT_BATCH_ITEM_FAILURES
|
|
471
|
+
]
|
|
472
|
+
if mapping.get("BatchSize") != batch_size:
|
|
473
|
+
changes["BatchSize"] = batch_size
|
|
474
|
+
if not changes:
|
|
475
|
+
continue
|
|
476
|
+
for key, value in changes.items():
|
|
477
|
+
print(
|
|
478
|
+
f" ~ event source mapping {mapping['UUID']}: {key} "
|
|
479
|
+
f"{mapping.get(key)!r} -> {value!r}"
|
|
480
|
+
)
|
|
481
|
+
lambda_client.update_event_source_mapping(UUID=mapping["UUID"], **changes)
|
|
482
|
+
updated.append(mapping["UUID"])
|
|
483
|
+
return {"created": False, "updated": updated}
|
|
484
|
+
|
|
485
|
+
|
|
379
486
|
def setup_sqs_queue_and_permissions(
|
|
380
487
|
*,
|
|
381
488
|
account_id: str,
|
|
@@ -389,11 +496,28 @@ def setup_sqs_queue_and_permissions(
|
|
|
389
496
|
sqs_resource=None,
|
|
390
497
|
lambda_client=None,
|
|
391
498
|
policy_json: Optional[str] = None,
|
|
392
|
-
|
|
499
|
+
function_timeout: Optional[int] = None,
|
|
500
|
+
visibility_timeout_multiplier: int = 6,
|
|
501
|
+
dlq_enabled: bool = True,
|
|
502
|
+
dlq_max_receive_count: int = 3,
|
|
503
|
+
dlq_retention_seconds: int = 1209600,
|
|
504
|
+
batch_size: int = 1,
|
|
505
|
+
) -> dict:
|
|
506
|
+
"""Reconcile the work queue, its DLQ and the handler's mapping.
|
|
507
|
+
|
|
508
|
+
- DLQ ``{queue_name}-dlq`` created by default, redrive maxReceiveCount 3;
|
|
509
|
+
- VisibilityTimeout = clamp(handler timeout x 6, 30 s, 12 h) — the
|
|
510
|
+
handler's ``Timeout`` unless ``function_timeout`` is given;
|
|
511
|
+
- queue policy scoped to the producer and handler roles;
|
|
512
|
+
- mapping with ``ReportBatchItemFailures`` (created or corrected).
|
|
513
|
+
|
|
514
|
+
The attribute diff is printed. ``sqs_resource`` is accepted for
|
|
515
|
+
backwards compatibility and unused. Returns
|
|
516
|
+
``{"queue_arn", "dlq_arn", "attributes_changed", "mapping"}``.
|
|
517
|
+
"""
|
|
518
|
+
del sqs_resource
|
|
393
519
|
if sqs_client is None:
|
|
394
520
|
sqs_client = boto3.client("sqs")
|
|
395
|
-
if sqs_resource is None:
|
|
396
|
-
sqs_resource = boto3.resource("sqs")
|
|
397
521
|
if lambda_client is None:
|
|
398
522
|
lambda_client = boto3.client("lambda")
|
|
399
523
|
|
|
@@ -401,28 +525,58 @@ def setup_sqs_queue_and_permissions(
|
|
|
401
525
|
policy_json = build_sqs_queue_policy(
|
|
402
526
|
account_id, region, queue_name, queue_producers, queue_handler
|
|
403
527
|
)
|
|
528
|
+
if function_timeout is None:
|
|
529
|
+
function_timeout = queue_handler.get("Timeout") or 900
|
|
530
|
+
visibility = queue_visibility_timeout(
|
|
531
|
+
function_timeout, visibility_timeout_multiplier
|
|
532
|
+
)
|
|
404
533
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
534
|
+
desired = {"Policy": policy_json, "VisibilityTimeout": str(visibility)}
|
|
535
|
+
dlq_arn = None
|
|
536
|
+
if dlq_enabled:
|
|
537
|
+
dlq_arn = _ensure_dlq_queue(
|
|
538
|
+
sqs_client, f"{queue_name}-dlq", dlq_retention_seconds
|
|
410
539
|
)
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
QueueName=queue_name,
|
|
414
|
-
Attributes={"Policy": policy_json, "VisibilityTimeout": "900"},
|
|
540
|
+
desired["RedrivePolicy"] = json.dumps(
|
|
541
|
+
{"maxReceiveCount": dlq_max_receive_count, "deadLetterTargetArn": dlq_arn}
|
|
415
542
|
)
|
|
416
543
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
544
|
+
queue_url = _queue_url_or_none(sqs_client, queue_name)
|
|
545
|
+
changed = []
|
|
546
|
+
if queue_url is None:
|
|
547
|
+
print(f" + queue {queue_name}: {sorted(desired)}")
|
|
548
|
+
queue_url = sqs_client.create_queue(QueueName=queue_name, Attributes=desired)[
|
|
549
|
+
"QueueUrl"
|
|
550
|
+
]
|
|
551
|
+
changed = sorted(desired)
|
|
552
|
+
else:
|
|
553
|
+
current = sqs_client.get_queue_attributes(
|
|
554
|
+
QueueUrl=queue_url, AttributeNames=["All"]
|
|
555
|
+
).get("Attributes", {})
|
|
556
|
+
delta = {
|
|
557
|
+
name: value
|
|
558
|
+
for name, value in desired.items()
|
|
559
|
+
if _normalized_attribute(name, current.get(name))
|
|
560
|
+
!= _normalized_attribute(name, value)
|
|
561
|
+
}
|
|
562
|
+
for name in sorted(delta):
|
|
563
|
+
if name == "Policy":
|
|
564
|
+
print(f" ~ queue {queue_name}: Policy updated")
|
|
565
|
+
else:
|
|
566
|
+
print(
|
|
567
|
+
f" ~ queue {queue_name}: {name} {current.get(name)!r} -> {delta[name]!r}"
|
|
568
|
+
)
|
|
569
|
+
if delta:
|
|
570
|
+
sqs_client.set_queue_attributes(QueueUrl=queue_url, Attributes=delta)
|
|
571
|
+
changed = sorted(delta)
|
|
572
|
+
|
|
573
|
+
queue_arn = f"arn:aws:sqs:{region}:{account_id}:{queue_name}"
|
|
574
|
+
mapping = ensure_queue_event_source_mapping(
|
|
575
|
+
lambda_client,
|
|
576
|
+
queue_arn=queue_arn,
|
|
577
|
+
function_name=queue_handler["FunctionName"],
|
|
578
|
+
batch_size=batch_size,
|
|
579
|
+
)
|
|
426
580
|
|
|
427
581
|
if use_reserved_concurrency:
|
|
428
582
|
reserved = 10 if stage == "production" else 3
|
|
@@ -443,6 +597,13 @@ def setup_sqs_queue_and_permissions(
|
|
|
443
597
|
except Exception:
|
|
444
598
|
pass
|
|
445
599
|
|
|
600
|
+
return {
|
|
601
|
+
"queue_arn": queue_arn,
|
|
602
|
+
"dlq_arn": dlq_arn,
|
|
603
|
+
"attributes_changed": changed,
|
|
604
|
+
"mapping": mapping,
|
|
605
|
+
}
|
|
606
|
+
|
|
446
607
|
|
|
447
608
|
def ensure_dlq(
|
|
448
609
|
queue_name: str,
|
|
@@ -453,30 +614,18 @@ def ensure_dlq(
|
|
|
453
614
|
) -> Optional[str]:
|
|
454
615
|
"""Idempotently create `{queue_name}-dlq` and attach a redrive policy to
|
|
455
616
|
the work queue. Returns the DLQ ARN, or None when the main queue is absent.
|
|
617
|
+
|
|
618
|
+
``setup_sqs_queue_and_permissions`` now does this itself by default; this
|
|
619
|
+
stays for build scripts that call it directly.
|
|
456
620
|
"""
|
|
457
621
|
if sqs_client is None:
|
|
458
622
|
sqs_client = boto3.client("sqs")
|
|
459
623
|
|
|
460
624
|
dlq_name = f"{queue_name}-dlq"
|
|
625
|
+
dlq_arn = _ensure_dlq_queue(sqs_client, dlq_name, retention_seconds)
|
|
461
626
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
print(f"[INFO] DLQ already exists: {dlq_name}")
|
|
465
|
-
except sqs_client.exceptions.QueueDoesNotExist:
|
|
466
|
-
resp = sqs_client.create_queue(
|
|
467
|
-
QueueName=dlq_name,
|
|
468
|
-
Attributes={"MessageRetentionPeriod": str(retention_seconds)},
|
|
469
|
-
)
|
|
470
|
-
dlq_url = resp["QueueUrl"]
|
|
471
|
-
print(f"[INFO] Created DLQ: {dlq_name}")
|
|
472
|
-
|
|
473
|
-
dlq_arn = sqs_client.get_queue_attributes(
|
|
474
|
-
QueueUrl=dlq_url, AttributeNames=["QueueArn"]
|
|
475
|
-
)["Attributes"]["QueueArn"]
|
|
476
|
-
|
|
477
|
-
try:
|
|
478
|
-
main_url = sqs_client.get_queue_url(QueueName=queue_name)["QueueUrl"]
|
|
479
|
-
except sqs_client.exceptions.QueueDoesNotExist:
|
|
627
|
+
main_url = _queue_url_or_none(sqs_client, queue_name)
|
|
628
|
+
if main_url is None:
|
|
480
629
|
print(f"[WARN] Main queue {queue_name!r} not found; skipping redrive policy.")
|
|
481
630
|
return None
|
|
482
631
|
|