velocity-python 0.1.85__tar.gz → 0.1.87__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.85/src/velocity_python.egg-info → velocity_python-0.1.87}/PKG-INFO +1 -1
- {velocity_python-0.1.85 → velocity_python-0.1.87}/pyproject.toml +1 -1
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/__init__.py +1 -1
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/__init__.py +11 -2
- velocity_python-0.1.87/src/velocity/aws/amplify.py +234 -0
- velocity_python-0.1.87/src/velocity/aws/amplify_build.py +160 -0
- velocity_python-0.1.87/src/velocity/aws/deploy/__init__.py +75 -0
- velocity_python-0.1.87/src/velocity/aws/deploy/config.py +65 -0
- velocity_python-0.1.87/src/velocity/aws/deploy/operations.py +742 -0
- velocity_python-0.1.87/src/velocity/aws/deploy/policies.py +303 -0
- velocity_python-0.1.87/src/velocity/aws/deploy/reconcile.py +220 -0
- velocity_python-0.1.87/src/velocity/aws/deploy/targets.py +189 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/handlers/mixins/data_service.py +36 -1
- velocity_python-0.1.87/src/velocity/aws/tests/test_amplify_build_policy_size.py +108 -0
- velocity_python-0.1.87/src/velocity/aws/tests/test_amplify_build_shim.py +65 -0
- velocity_python-0.1.87/src/velocity/aws/tests/test_deploy_policies.py +117 -0
- velocity_python-0.1.87/src/velocity/aws/tests/test_deploy_reconcile.py +198 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87/src/velocity_python.egg-info}/PKG-INFO +1 -1
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity_python.egg-info/SOURCES.txt +11 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_amplify_build.py +40 -51
- velocity_python-0.1.87/tests/test_delete_hook_fk_blocked.py +154 -0
- velocity_python-0.1.85/src/velocity/aws/amplify.py +0 -478
- velocity_python-0.1.85/src/velocity/aws/amplify_build.py +0 -761
- {velocity_python-0.1.85 → velocity_python-0.1.87}/LICENSE +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/README.md +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/setup.cfg +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/assets/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/assets/backfill.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/assets/indexing.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/assets/references.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/assets/service.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/assets/usage_index.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/dirty_pipeline.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/handlers/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/handlers/base_handler.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/handlers/context.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/handlers/context_factory.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/handlers/exceptions.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/handlers/lambda_handler.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/handlers/masquerade.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/handlers/mixins/web_handler.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/handlers/perf.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/handlers/response.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/handlers/sqs_handler.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/s3.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/ssm_config.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/tests/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/tests/test_base_handler_error_response.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/aws/tests/test_response.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/core/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/core/async_support.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/core/column.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/core/database.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/core/decorators.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/core/engine.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/core/jsonproxy.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/core/result.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/core/row.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/core/sequence.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/core/table.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/core/transaction.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/core/view.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/exceptions.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/maintenance.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/migrations.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/base/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/base/initializer.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/base/operators.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/base/sql.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/base/types.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/mysql/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/mysql/operators.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/mysql/reserved.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/mysql/sql.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/mysql/types.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/postgres/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/postgres/operators.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/postgres/reserved.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/postgres/sql.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/postgres/types.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/sqlite/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/sqlite/operators.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/sqlite/reserved.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/sqlite/sql.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/sqlite/types.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/sqlserver/operators.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/sqlserver/sql.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/sqlserver/types.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/servers/tablehelper.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/common_db_test.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/postgres/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/postgres/common.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/postgres/conftest.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/postgres/test_column.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/postgres/test_connections.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/postgres/test_database.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/postgres/test_engine.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/postgres/test_imports.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/postgres/test_result.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/postgres/test_row.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/postgres/test_table.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/sql/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/sql/common.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/test_db_utils.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/test_postgres.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/test_result_caching.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/test_sql_builder.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/test_tablehelper.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/tests/test_view_helper.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/db/utils.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/logging.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/misc/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/misc/conv/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/misc/conv/iconv.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/misc/conv/oconv.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/misc/db.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/misc/export.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/misc/format.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/misc/mail.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/misc/merge.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/misc/pdf.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/misc/tests/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/misc/tests/test_db.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/misc/tests/test_fix.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/misc/tests/test_format.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/misc/tests/test_iconv.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/misc/tests/test_merge.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/misc/tests/test_oconv.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/misc/tests/test_original_error.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/misc/tests/test_timer.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/misc/timer.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/misc/tools.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/nosql/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/nosql/core/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/nosql/core/capabilities.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/nosql/core/store.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/nosql/core/where.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/nosql/exceptions.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/nosql/servers/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/nosql/servers/base/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/nosql/servers/cassandra/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/nosql/servers/cassandra/translation.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/nosql/servers/couchbase/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/nosql/servers/couchbase/translation.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/nosql/servers/dynamodb/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/nosql/servers/dynamodb/translation.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/nosql/servers/elasticsearch/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/nosql/servers/elasticsearch/translation.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/nosql/servers/memory/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/nosql/servers/mongodb/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/nosql/servers/mongodb/translation.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/nosql/servers/redis/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/payment/__init__.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/payment/authorizenet_adapter.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/payment/authorizenet_mirror.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/payment/base_adapter.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/payment/braintree_adapter.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/payment/braintree_mirror.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/payment/charge_rules.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/payment/stripe_adapter.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity/payment/stripe_mirror.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity_python.egg-info/dependency_links.txt +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity_python.egg-info/entry_points.txt +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity_python.egg-info/requires.txt +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/src/velocity_python.egg-info/top_level.txt +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_activity_tracking_aborted_tx.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_asset_indexing.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_asset_references.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_assets_service.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_async_support.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_batch_operations.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_column_tx_arg.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_concurrency_safety.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_connection_pool.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_connection_resilience.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_context_job_descriptions.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_db_credentials_ssm_cascade.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_db_maintenance.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_decorators.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_dirty_pipeline_fast_path.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_email_processing.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_enqueue_send_failures.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_get_cognito_user_provider.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_http_handler_rollback.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_iconv_money_to_cents.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_identifier_injection_guard.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_json_columns.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_jsonb_dict_adapter.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_lambda_handler.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_lambda_handler_auth.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_lambda_handler_masquerade.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_masquerade_grant.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_mixins_import.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_n_plus_one.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_nosql_dynamodb.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_nosql_memory.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_nosql_mongodb.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_nosql_redis.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_nosql_translation.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_nosql_where.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_observability.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_payment_authorizenet_adapter.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_payment_braintree_adapter.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_payment_braintree_mirror.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_payment_mirror_empty_table.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_payment_profile_sorting.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_payment_stripe_adapter.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_pdf.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_prepared_statements.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_psycopg3_upgrade.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_query_cache.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_restricted_direct_tables.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_retry_side_effect_guard.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_return_default_safety.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_row_batch_update.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_row_cache_staleness.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_schema_migrations.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_security_hardening.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_server_cursor.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_single_autocommit_safety.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_spreadsheet_functions.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_sqlite_backend.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_sqs_per_record_transactions.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_ssm_config.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_store_user_data.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_sys_modified_count_postgres_demo.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_table_alter.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_transaction_class_wrapping.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_transaction_commit_and_ownership.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_transaction_edge_cases.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_where_clause_validation.py +0 -0
- {velocity_python-0.1.85 → velocity_python-0.1.87}/tests/test_write_hook_create_flow.py +0 -0
|
@@ -23,12 +23,18 @@ class AWS(object):
|
|
|
23
23
|
# Lazy-load heavy modules (boto3-dependent) on first access
|
|
24
24
|
_LAZY_ATTRS = {
|
|
25
25
|
"AmplifyProject": "velocity.aws.amplify",
|
|
26
|
+
# Legacy deployment API (shim over velocity.aws.deploy; legacy-union
|
|
27
|
+
# policy mode). New projects: import from velocity.aws.deploy directly.
|
|
26
28
|
"BackendDeploymentConfig": "velocity.aws.amplify_build",
|
|
27
|
-
"build_environment_variables": "velocity.aws.amplify_build",
|
|
28
29
|
"get_amplify_app_id_and_branch": "velocity.aws.amplify_build",
|
|
29
30
|
"initialize_build_environment": "velocity.aws.amplify_build",
|
|
30
31
|
"run_backend_deployment": "velocity.aws.amplify_build",
|
|
31
32
|
"update_lambda_layers_to_latest": "velocity.aws.amplify_build",
|
|
33
|
+
# Platform-neutral deployment API
|
|
34
|
+
"AmplifyTarget": "velocity.aws.deploy",
|
|
35
|
+
"DeploymentConfig": "velocity.aws.deploy",
|
|
36
|
+
"PlainTarget": "velocity.aws.deploy",
|
|
37
|
+
"run_deployment": "velocity.aws.deploy",
|
|
32
38
|
# S3 utilities
|
|
33
39
|
"generate_presigned_get": "velocity.aws.s3",
|
|
34
40
|
"generate_presigned_put": "velocity.aws.s3",
|
|
@@ -71,12 +77,15 @@ def __getattr__(name: str):
|
|
|
71
77
|
|
|
72
78
|
__all__ = [
|
|
73
79
|
"AmplifyProject",
|
|
80
|
+
"AmplifyTarget",
|
|
74
81
|
"AWS",
|
|
75
82
|
"BackendDeploymentConfig",
|
|
76
|
-
"
|
|
83
|
+
"DeploymentConfig",
|
|
84
|
+
"PlainTarget",
|
|
77
85
|
"get_amplify_app_id_and_branch",
|
|
78
86
|
"initialize_build_environment",
|
|
79
87
|
"run_backend_deployment",
|
|
88
|
+
"run_deployment",
|
|
80
89
|
"update_lambda_layers_to_latest",
|
|
81
90
|
# S3 utilities
|
|
82
91
|
"generate_presigned_get",
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import boto3
|
|
2
|
+
import time
|
|
3
|
+
from collections.abc import Mapping, Sequence
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AmplifyProject:
|
|
7
|
+
def __init__(self, app_id: str):
|
|
8
|
+
self.app_id = app_id
|
|
9
|
+
self.amplify_client = boto3.client("amplify")
|
|
10
|
+
self.lambda_client = boto3.client("lambda")
|
|
11
|
+
self.sqs_client = boto3.client("sqs")
|
|
12
|
+
self.sqs_resource = boto3.resource("sqs")
|
|
13
|
+
|
|
14
|
+
# Verify app exists
|
|
15
|
+
try:
|
|
16
|
+
response = self.amplify_client.get_app(appId=self.app_id)
|
|
17
|
+
self.app_name = response["app"]["name"]
|
|
18
|
+
except self.amplify_client.exceptions.NotFoundException:
|
|
19
|
+
raise ValueError(f"Amplify app with ID '{self.app_id}' does not exist.")
|
|
20
|
+
|
|
21
|
+
def get_app_name(self):
|
|
22
|
+
return self.app_name
|
|
23
|
+
|
|
24
|
+
def get_region(self):
|
|
25
|
+
return self.amplify_client.meta.region_name
|
|
26
|
+
|
|
27
|
+
def get_account_id(self):
|
|
28
|
+
sts_client = boto3.client("sts")
|
|
29
|
+
return sts_client.get_caller_identity()["Account"]
|
|
30
|
+
|
|
31
|
+
def list_backend_branches(self):
|
|
32
|
+
branches = []
|
|
33
|
+
paginator = self.amplify_client.get_paginator("list_branches")
|
|
34
|
+
for page in paginator.paginate(appId=self.app_id):
|
|
35
|
+
for branch in page.get("branches", []):
|
|
36
|
+
branches.append(branch["branchName"])
|
|
37
|
+
return branches
|
|
38
|
+
|
|
39
|
+
def filtered_env_vars(self, raw_env_vars):
|
|
40
|
+
return {k: v for k, v in raw_env_vars.items() if not k.startswith("_")}
|
|
41
|
+
|
|
42
|
+
def get_merged_env_vars(self, branch):
|
|
43
|
+
all_vars = {}
|
|
44
|
+
|
|
45
|
+
# Global env vars
|
|
46
|
+
app_response = self.amplify_client.get_app(appId=self.app_id)
|
|
47
|
+
global_vars = self.filtered_env_vars(
|
|
48
|
+
app_response["app"].get("environmentVariables", {})
|
|
49
|
+
)
|
|
50
|
+
# print("📦 Global env vars (All branches):")
|
|
51
|
+
# for k, v in global_vars.items():
|
|
52
|
+
# print(f" {k}: {v}")
|
|
53
|
+
all_vars.update(global_vars)
|
|
54
|
+
|
|
55
|
+
# Branch-specific vars
|
|
56
|
+
try:
|
|
57
|
+
branch_response = self.amplify_client.get_branch(
|
|
58
|
+
appId=self.app_id, branchName=branch
|
|
59
|
+
)
|
|
60
|
+
branch_vars = self.filtered_env_vars(
|
|
61
|
+
branch_response["branch"].get("environmentVariables", {})
|
|
62
|
+
)
|
|
63
|
+
# print(f"🌿 Branch-specific env vars for '{branch}':")
|
|
64
|
+
# for k, v in branch_vars.items():
|
|
65
|
+
# print(f" {k}: {v}")
|
|
66
|
+
all_vars.update(branch_vars)
|
|
67
|
+
except self.amplify_client.exceptions.BadRequestException:
|
|
68
|
+
print(f"⚠️ Branch '{branch}' not found. Skipping branch-level overrides.")
|
|
69
|
+
|
|
70
|
+
return all_vars
|
|
71
|
+
|
|
72
|
+
def list_lambda_functions_filtered(self, env_name):
|
|
73
|
+
"""
|
|
74
|
+
List Lambda functions that belong to this Amplify app and environment.
|
|
75
|
+
Filters by user:Application tag, checking both exact app name and sanitized version.
|
|
76
|
+
"""
|
|
77
|
+
from velocity.aws.deploy.operations import list_functions_by_tag
|
|
78
|
+
|
|
79
|
+
return list_functions_by_tag(
|
|
80
|
+
self.lambda_client, app_name=self.app_name, stage=env_name
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
def update_lambda_function(
|
|
84
|
+
self,
|
|
85
|
+
function_name,
|
|
86
|
+
**kwargs,
|
|
87
|
+
):
|
|
88
|
+
from velocity.aws.deploy.operations import update_lambda_function_config
|
|
89
|
+
|
|
90
|
+
return update_lambda_function_config(
|
|
91
|
+
self.lambda_client, function_name, **kwargs
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
def set_environment_variable(self, key: str, value: str, branch: str = None):
|
|
95
|
+
if branch:
|
|
96
|
+
response = self.amplify_client.get_branch(
|
|
97
|
+
appId=self.app_id, branchName=branch
|
|
98
|
+
)
|
|
99
|
+
env_vars = response["branch"].get("environmentVariables", {})
|
|
100
|
+
env_vars[key] = value
|
|
101
|
+
self.amplify_client.update_branch(
|
|
102
|
+
appId=self.app_id, branchName=branch, environmentVariables=env_vars
|
|
103
|
+
)
|
|
104
|
+
else:
|
|
105
|
+
response = self.amplify_client.get_app(appId=self.app_id)
|
|
106
|
+
env_vars = response["app"].get("environmentVariables", {})
|
|
107
|
+
env_vars[key] = value
|
|
108
|
+
self.amplify_client.update_app(
|
|
109
|
+
appId=self.app_id, environmentVariables=env_vars
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
def update_custom_redirect_rules(self):
|
|
113
|
+
# Extensions served as real files; every other path is rewritten to the
|
|
114
|
+
# SPA. Anything NOT listed here (including static .html pages in
|
|
115
|
+
# public/) is invisible on the deployed site.
|
|
116
|
+
srv_list = [
|
|
117
|
+
"css",
|
|
118
|
+
"gif",
|
|
119
|
+
"html",
|
|
120
|
+
"ico",
|
|
121
|
+
"jpeg",
|
|
122
|
+
"jpg",
|
|
123
|
+
"js",
|
|
124
|
+
"json",
|
|
125
|
+
"map",
|
|
126
|
+
"otf",
|
|
127
|
+
"png",
|
|
128
|
+
"svg",
|
|
129
|
+
"ttf",
|
|
130
|
+
"txt",
|
|
131
|
+
"webp",
|
|
132
|
+
"woff",
|
|
133
|
+
"xml",
|
|
134
|
+
"pdf",
|
|
135
|
+
]
|
|
136
|
+
self.amplify_client.update_app(
|
|
137
|
+
appId=self.app_id,
|
|
138
|
+
customRules=[
|
|
139
|
+
{"source": "/<*>", "target": "/index.html", "status": "404-200"},
|
|
140
|
+
{
|
|
141
|
+
"source": f'</^[^.]+$|\\.(?!({"|".join(srv_list)})$)([^.]+$)/>',
|
|
142
|
+
"target": "/",
|
|
143
|
+
"status": "200",
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
def get_sqs_policy_template(
|
|
149
|
+
self,
|
|
150
|
+
queue_name: str,
|
|
151
|
+
queue_producers: Sequence[Mapping[str, str]],
|
|
152
|
+
queue_handler: Mapping[str, str],
|
|
153
|
+
) -> str:
|
|
154
|
+
from velocity.aws.deploy.operations import build_sqs_queue_policy
|
|
155
|
+
|
|
156
|
+
return build_sqs_queue_policy(
|
|
157
|
+
self.get_account_id(),
|
|
158
|
+
self.get_region(),
|
|
159
|
+
queue_name,
|
|
160
|
+
queue_producers,
|
|
161
|
+
queue_handler,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
def get_lambda_vpc_policy_template(self):
|
|
165
|
+
from velocity.aws.deploy.policies import baseline_lambda_policy_document
|
|
166
|
+
|
|
167
|
+
return baseline_lambda_policy_document(self.get_account_id())
|
|
168
|
+
|
|
169
|
+
def setup_sqs_queue_and_permissions(
|
|
170
|
+
self,
|
|
171
|
+
branch: str,
|
|
172
|
+
queue_name: str,
|
|
173
|
+
queue_producers: Sequence[Mapping[str, str]],
|
|
174
|
+
queue_handler: Mapping[str, str],
|
|
175
|
+
use_reserved_concurrency: bool = True,
|
|
176
|
+
) -> None:
|
|
177
|
+
from velocity.aws.deploy.operations import setup_sqs_queue_and_permissions
|
|
178
|
+
|
|
179
|
+
setup_sqs_queue_and_permissions(
|
|
180
|
+
account_id=self.get_account_id(),
|
|
181
|
+
region=self.get_region(),
|
|
182
|
+
stage=branch,
|
|
183
|
+
queue_name=queue_name,
|
|
184
|
+
queue_producers=queue_producers,
|
|
185
|
+
queue_handler=queue_handler,
|
|
186
|
+
use_reserved_concurrency=use_reserved_concurrency,
|
|
187
|
+
sqs_client=self.sqs_client,
|
|
188
|
+
sqs_resource=self.sqs_resource,
|
|
189
|
+
lambda_client=self.lambda_client,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
def check_policies(self, function):
|
|
193
|
+
# Attach a role policy
|
|
194
|
+
iam_client = boto3.client("iam")
|
|
195
|
+
response = iam_client.list_attached_role_policies(
|
|
196
|
+
RoleName=function["Role"].split("/")[1]
|
|
197
|
+
)
|
|
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
|
+
|
|
219
|
+
def sync(self, branch):
|
|
220
|
+
raise RuntimeError(
|
|
221
|
+
"AmplifyProject.sync() is retired. Lambda environment variables are no "
|
|
222
|
+
"longer copied from Amplify configuration. Use SSM-backed runtime "
|
|
223
|
+
"configuration instead."
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def main():
|
|
228
|
+
app_id = "d3c209q3ri53mk"
|
|
229
|
+
app = AmplifyProject(app_id)
|
|
230
|
+
print(app.list_backend_branches())
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
if __name__ == "__main__":
|
|
234
|
+
main()
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""Backwards-compatibility shim over velocity.aws.deploy.
|
|
2
|
+
|
|
3
|
+
The deployment engine moved to the platform-neutral velocity.aws.deploy
|
|
4
|
+
package (config / targets / policies / operations / reconcile). This module
|
|
5
|
+
keeps every name and signature the existing project build.py files import,
|
|
6
|
+
with pre-refactor behavior:
|
|
7
|
+
|
|
8
|
+
- run_backend_deployment() runs in "legacy-union" policy mode (config extras
|
|
9
|
+
unioned into the shared lambda-vpc-execution policy). Projects migrated to
|
|
10
|
+
velocity.aws.deploy.run_deployment() get per-app reconciled policies.
|
|
11
|
+
|
|
12
|
+
New projects should import from velocity.aws.deploy directly.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import time
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Optional, Sequence, Union
|
|
18
|
+
|
|
19
|
+
import boto3
|
|
20
|
+
|
|
21
|
+
from velocity.aws.amplify import AmplifyProject
|
|
22
|
+
from velocity.aws.deploy.config import BackendDeploymentConfig, DeploymentConfig
|
|
23
|
+
from velocity.aws.deploy.policies import (
|
|
24
|
+
IAM_MANAGED_POLICY_MAX_CHARS,
|
|
25
|
+
ManagedPolicySizeError,
|
|
26
|
+
_canonical_statement,
|
|
27
|
+
_ensure_policy_document_size,
|
|
28
|
+
_merge_policy_statements,
|
|
29
|
+
_normalize_policy_document,
|
|
30
|
+
_policy_document_char_count,
|
|
31
|
+
_serialize_policy_document,
|
|
32
|
+
attach_role_policies,
|
|
33
|
+
baseline_lambda_policy_document,
|
|
34
|
+
sync_managed_policy_document,
|
|
35
|
+
)
|
|
36
|
+
from velocity.aws.deploy.operations import (
|
|
37
|
+
THROTTLE_ERROR_CODES,
|
|
38
|
+
ensure_cloudwatch_log_group,
|
|
39
|
+
resolve_latest_layer_arns,
|
|
40
|
+
retryable_call,
|
|
41
|
+
)
|
|
42
|
+
from velocity.aws.deploy.operations import (
|
|
43
|
+
set_cloudwatch_logging as _set_cloudwatch_logging_op,
|
|
44
|
+
)
|
|
45
|
+
from velocity.aws.deploy.reconcile import AWS_SQS_FULL_ACCESS_ARN, run_deployment
|
|
46
|
+
from velocity.aws.deploy.reconcile import (
|
|
47
|
+
update_lambda_layers_to_latest as _update_layers_new,
|
|
48
|
+
)
|
|
49
|
+
from velocity.aws.deploy.targets import (
|
|
50
|
+
ANSI_ESCAPE_RE,
|
|
51
|
+
get_amplify_app_id_and_branch,
|
|
52
|
+
initialize_build_environment,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Legacy private alias kept for any direct importers.
|
|
56
|
+
_sync_managed_policy_document = sync_managed_policy_document
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class _AmplifyProjectTarget:
|
|
60
|
+
"""Adapts a legacy AmplifyProject (+ branch) to the DeployTarget shape."""
|
|
61
|
+
|
|
62
|
+
def __init__(self, app: AmplifyProject, branch: str):
|
|
63
|
+
self._app = app
|
|
64
|
+
self.app_name = app.app_name
|
|
65
|
+
self.stage = branch
|
|
66
|
+
|
|
67
|
+
def account_id(self) -> str:
|
|
68
|
+
return self._app.get_account_id()
|
|
69
|
+
|
|
70
|
+
def region(self) -> str:
|
|
71
|
+
return self._app.get_region()
|
|
72
|
+
|
|
73
|
+
def list_functions(self):
|
|
74
|
+
return self._app.list_lambda_functions_filtered(self.stage)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def ensure_lambda_policies_and_attach(
|
|
78
|
+
app: AmplifyProject,
|
|
79
|
+
function,
|
|
80
|
+
config: Optional[DeploymentConfig] = None,
|
|
81
|
+
iam_client=None,
|
|
82
|
+
) -> None:
|
|
83
|
+
"""Legacy per-function policy ensure: union document into the shared
|
|
84
|
+
lambda-vpc-execution policy, then attach it + AmazonSQSFullAccess."""
|
|
85
|
+
if iam_client is None:
|
|
86
|
+
iam_client = boto3.client("iam")
|
|
87
|
+
|
|
88
|
+
account_id = app.get_account_id()
|
|
89
|
+
policy_arn = f"arn:aws:iam::{account_id}:policy/lambda-vpc-execution"
|
|
90
|
+
policy_document = baseline_lambda_policy_document(account_id)
|
|
91
|
+
if config is not None:
|
|
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])
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def set_cloudwatch_logging(
|
|
113
|
+
app: AmplifyProject,
|
|
114
|
+
function,
|
|
115
|
+
branch: str,
|
|
116
|
+
lambda_client=None,
|
|
117
|
+
logs_client=None,
|
|
118
|
+
) -> bool:
|
|
119
|
+
return _set_cloudwatch_logging_op(
|
|
120
|
+
account_id=app.get_account_id(),
|
|
121
|
+
region=app.get_region(),
|
|
122
|
+
function=function,
|
|
123
|
+
stage=branch,
|
|
124
|
+
lambda_client=lambda_client,
|
|
125
|
+
logs_client=logs_client,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def run_backend_deployment(
|
|
130
|
+
app_id: str,
|
|
131
|
+
branch: str,
|
|
132
|
+
config: DeploymentConfig,
|
|
133
|
+
project_root: Union[str, Path] = ".",
|
|
134
|
+
app: Optional[AmplifyProject] = None,
|
|
135
|
+
lambda_client=None,
|
|
136
|
+
logs_client=None,
|
|
137
|
+
) -> None:
|
|
138
|
+
if app is None:
|
|
139
|
+
app = AmplifyProject(app_id)
|
|
140
|
+
target = _AmplifyProjectTarget(app, branch)
|
|
141
|
+
run_deployment(
|
|
142
|
+
target,
|
|
143
|
+
config,
|
|
144
|
+
policy_mode="legacy-union",
|
|
145
|
+
lambda_client=lambda_client,
|
|
146
|
+
logs_client=logs_client,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def update_lambda_layers_to_latest(
|
|
151
|
+
app_id: str,
|
|
152
|
+
branch: str,
|
|
153
|
+
function_names: Optional[Sequence[str]] = None,
|
|
154
|
+
app: Optional[AmplifyProject] = None,
|
|
155
|
+
lambda_client=None,
|
|
156
|
+
) -> None:
|
|
157
|
+
if app is None:
|
|
158
|
+
app = AmplifyProject(app_id)
|
|
159
|
+
target = _AmplifyProjectTarget(app, branch)
|
|
160
|
+
_update_layers_new(target, function_names=function_names, lambda_client=lambda_client)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""velocity.aws.deploy — platform-neutral backend deployment.
|
|
2
|
+
|
|
3
|
+
Public API:
|
|
4
|
+
|
|
5
|
+
from velocity.aws.deploy import (
|
|
6
|
+
DeploymentConfig, AmplifyTarget, PlainTarget,
|
|
7
|
+
run_deployment, update_lambda_layers_to_latest,
|
|
8
|
+
initialize_build_environment, get_amplify_app_id_and_branch,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
target = AmplifyTarget(PROJECT_ROOT) # existing Amplify apps
|
|
12
|
+
target = PlainTarget("MyApp", stage="demo") # no Amplify anywhere
|
|
13
|
+
run_deployment(target, config)
|
|
14
|
+
|
|
15
|
+
See docs/deploy.md in the velocity-python repo.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from velocity.aws.deploy.config import BackendDeploymentConfig, DeploymentConfig
|
|
19
|
+
from velocity.aws.deploy.policies import (
|
|
20
|
+
IAM_MANAGED_POLICY_MAX_CHARS,
|
|
21
|
+
ManagedPolicySizeError,
|
|
22
|
+
app_policy_name,
|
|
23
|
+
baseline_lambda_policy_document,
|
|
24
|
+
ensure_app_policy,
|
|
25
|
+
sync_managed_policy_document,
|
|
26
|
+
)
|
|
27
|
+
from velocity.aws.deploy.operations import (
|
|
28
|
+
ensure_api_cors_header,
|
|
29
|
+
ensure_cloudwatch_log_group,
|
|
30
|
+
ensure_dlq,
|
|
31
|
+
ensure_identity_pool_developer_provider,
|
|
32
|
+
list_functions_by_tag,
|
|
33
|
+
resolve_latest_layer_arns,
|
|
34
|
+
retryable_call,
|
|
35
|
+
set_cloudwatch_logging,
|
|
36
|
+
setup_sqs_queue_and_permissions,
|
|
37
|
+
update_lambda_function_config,
|
|
38
|
+
)
|
|
39
|
+
from velocity.aws.deploy.targets import (
|
|
40
|
+
AmplifyTarget,
|
|
41
|
+
PlainTarget,
|
|
42
|
+
get_amplify_app_id_and_branch,
|
|
43
|
+
initialize_build_environment,
|
|
44
|
+
)
|
|
45
|
+
from velocity.aws.deploy.reconcile import (
|
|
46
|
+
run_deployment,
|
|
47
|
+
update_lambda_layers_to_latest,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
__all__ = [
|
|
51
|
+
"AmplifyTarget",
|
|
52
|
+
"BackendDeploymentConfig",
|
|
53
|
+
"DeploymentConfig",
|
|
54
|
+
"IAM_MANAGED_POLICY_MAX_CHARS",
|
|
55
|
+
"ManagedPolicySizeError",
|
|
56
|
+
"PlainTarget",
|
|
57
|
+
"app_policy_name",
|
|
58
|
+
"baseline_lambda_policy_document",
|
|
59
|
+
"ensure_api_cors_header",
|
|
60
|
+
"ensure_app_policy",
|
|
61
|
+
"ensure_cloudwatch_log_group",
|
|
62
|
+
"ensure_dlq",
|
|
63
|
+
"ensure_identity_pool_developer_provider",
|
|
64
|
+
"get_amplify_app_id_and_branch",
|
|
65
|
+
"initialize_build_environment",
|
|
66
|
+
"list_functions_by_tag",
|
|
67
|
+
"resolve_latest_layer_arns",
|
|
68
|
+
"retryable_call",
|
|
69
|
+
"run_deployment",
|
|
70
|
+
"set_cloudwatch_logging",
|
|
71
|
+
"setup_sqs_queue_and_permissions",
|
|
72
|
+
"sync_managed_policy_document",
|
|
73
|
+
"update_lambda_function_config",
|
|
74
|
+
"update_lambda_layers_to_latest",
|
|
75
|
+
]
|
|
@@ -0,0 +1,65 @@
|
|
|
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
|
+
def resolve_queue_name(self, stage: str) -> str:
|
|
33
|
+
return self.queue_name_template.format(branch=stage.lower(), raw_branch=stage)
|
|
34
|
+
|
|
35
|
+
def resolve_timeout(self, function_name: str) -> int:
|
|
36
|
+
if any(token in function_name for token in self.short_timeout_tokens):
|
|
37
|
+
return self.short_timeout_seconds
|
|
38
|
+
return self.default_timeout_seconds
|
|
39
|
+
|
|
40
|
+
def resolve_memory_size(self, function_name: str) -> Optional[int]:
|
|
41
|
+
for token, memory_size in self.memory_size_by_token.items():
|
|
42
|
+
if token in function_name:
|
|
43
|
+
return memory_size
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
def build_lambda_policy_document(
|
|
47
|
+
self, base_policy_document: Mapping[str, Any]
|
|
48
|
+
) -> Dict[str, Any]:
|
|
49
|
+
"""Legacy helper: base document + this config's extra statements.
|
|
50
|
+
|
|
51
|
+
Used only by the legacy shared-union policy mode (velocity.aws
|
|
52
|
+
amplify_build shim). The per-app policy mode reads
|
|
53
|
+
extra_lambda_policy_statements directly.
|
|
54
|
+
"""
|
|
55
|
+
policy_document = dict(base_policy_document)
|
|
56
|
+
statements = list(policy_document.get("Statement", []))
|
|
57
|
+
statements.extend(
|
|
58
|
+
dict(statement) for statement in self.extra_lambda_policy_statements
|
|
59
|
+
)
|
|
60
|
+
policy_document["Statement"] = statements
|
|
61
|
+
return policy_document
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# Backwards-compatible name used by existing build.py files.
|
|
65
|
+
BackendDeploymentConfig = DeploymentConfig
|