velocity-python 0.1.94__tar.gz → 0.1.95__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. {velocity_python-0.1.94 → velocity_python-0.1.95}/PKG-INFO +1 -1
  2. {velocity_python-0.1.94 → velocity_python-0.1.95}/pyproject.toml +1 -1
  3. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/__init__.py +1 -1
  4. velocity_python-0.1.95/src/velocity/aws/sms.py +237 -0
  5. velocity_python-0.1.95/src/velocity/aws/tests/test_sms.py +178 -0
  6. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity_python.egg-info/PKG-INFO +1 -1
  7. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity_python.egg-info/SOURCES.txt +2 -0
  8. {velocity_python-0.1.94 → velocity_python-0.1.95}/LICENSE +0 -0
  9. {velocity_python-0.1.94 → velocity_python-0.1.95}/README.md +0 -0
  10. {velocity_python-0.1.94 → velocity_python-0.1.95}/setup.cfg +0 -0
  11. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/__init__.py +0 -0
  12. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/amplify.py +0 -0
  13. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/amplify_build.py +0 -0
  14. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/assets/__init__.py +0 -0
  15. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/assets/backfill.py +0 -0
  16. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/assets/indexing.py +0 -0
  17. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/assets/references.py +0 -0
  18. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/assets/service.py +0 -0
  19. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/assets/usage_index.py +0 -0
  20. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/deploy/__init__.py +0 -0
  21. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/deploy/config.py +0 -0
  22. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/deploy/operations.py +0 -0
  23. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/deploy/policies.py +0 -0
  24. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/deploy/reconcile.py +0 -0
  25. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/deploy/targets.py +0 -0
  26. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/dirty_pipeline.py +0 -0
  27. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/handlers/__init__.py +0 -0
  28. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/handlers/base_handler.py +0 -0
  29. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/handlers/context.py +0 -0
  30. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/handlers/context_factory.py +0 -0
  31. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/handlers/exceptions.py +0 -0
  32. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/handlers/lambda_handler.py +0 -0
  33. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/handlers/masquerade.py +0 -0
  34. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
  35. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/handlers/mixins/data_service.py +0 -0
  36. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/handlers/mixins/job_handler.py +0 -0
  37. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/handlers/mixins/web_handler.py +0 -0
  38. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/handlers/perf.py +0 -0
  39. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/handlers/response.py +0 -0
  40. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/handlers/sqs_handler.py +0 -0
  41. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/job_results.py +0 -0
  42. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/s3.py +0 -0
  43. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/ssm_config.py +0 -0
  44. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/tests/__init__.py +0 -0
  45. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/tests/test_amplify_build_policy_size.py +0 -0
  46. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/tests/test_amplify_build_shim.py +0 -0
  47. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/tests/test_base_handler_error_response.py +0 -0
  48. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/tests/test_deploy_policies.py +0 -0
  49. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/tests/test_deploy_reconcile.py +0 -0
  50. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
  51. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/aws/tests/test_response.py +0 -0
  52. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/__init__.py +0 -0
  53. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/core/__init__.py +0 -0
  54. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/core/async_support.py +0 -0
  55. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/core/column.py +0 -0
  56. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/core/database.py +0 -0
  57. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/core/decorators.py +0 -0
  58. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/core/engine.py +0 -0
  59. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/core/jsonproxy.py +0 -0
  60. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/core/result.py +0 -0
  61. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/core/row.py +0 -0
  62. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/core/sequence.py +0 -0
  63. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/core/table.py +0 -0
  64. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/core/transaction.py +0 -0
  65. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/core/view.py +0 -0
  66. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/exceptions.py +0 -0
  67. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/maintenance.py +0 -0
  68. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/migrations.py +0 -0
  69. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/__init__.py +0 -0
  70. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/base/__init__.py +0 -0
  71. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/base/initializer.py +0 -0
  72. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/base/operators.py +0 -0
  73. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/base/sql.py +0 -0
  74. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/base/types.py +0 -0
  75. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/mysql/__init__.py +0 -0
  76. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/mysql/operators.py +0 -0
  77. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/mysql/reserved.py +0 -0
  78. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/mysql/sql.py +0 -0
  79. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/mysql/types.py +0 -0
  80. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/postgres/__init__.py +0 -0
  81. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/postgres/operators.py +0 -0
  82. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/postgres/reserved.py +0 -0
  83. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/postgres/sql.py +0 -0
  84. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/postgres/types.py +0 -0
  85. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/sqlite/__init__.py +0 -0
  86. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/sqlite/operators.py +0 -0
  87. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/sqlite/reserved.py +0 -0
  88. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/sqlite/sql.py +0 -0
  89. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/sqlite/types.py +0 -0
  90. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
  91. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/sqlserver/operators.py +0 -0
  92. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
  93. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/sqlserver/sql.py +0 -0
  94. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/sqlserver/types.py +0 -0
  95. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/servers/tablehelper.py +0 -0
  96. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/__init__.py +0 -0
  97. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/common_db_test.py +0 -0
  98. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/postgres/__init__.py +0 -0
  99. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/postgres/common.py +0 -0
  100. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/postgres/conftest.py +0 -0
  101. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/postgres/test_column.py +0 -0
  102. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/postgres/test_connections.py +0 -0
  103. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/postgres/test_database.py +0 -0
  104. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/postgres/test_engine.py +0 -0
  105. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
  106. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/postgres/test_imports.py +0 -0
  107. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/postgres/test_result.py +0 -0
  108. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/postgres/test_row.py +0 -0
  109. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
  110. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
  111. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
  112. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
  113. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
  114. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/postgres/test_table.py +0 -0
  115. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
  116. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
  117. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/sql/__init__.py +0 -0
  118. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/sql/common.py +0 -0
  119. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
  120. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
  121. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
  122. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/test_db_utils.py +0 -0
  123. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/test_postgres.py +0 -0
  124. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
  125. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
  126. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/test_result_caching.py +0 -0
  127. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
  128. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
  129. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
  130. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
  131. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/test_sql_builder.py +0 -0
  132. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/test_tablehelper.py +0 -0
  133. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/tests/test_view_helper.py +0 -0
  134. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/db/utils.py +0 -0
  135. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/logging.py +0 -0
  136. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/misc/__init__.py +0 -0
  137. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/misc/conv/__init__.py +0 -0
  138. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/misc/conv/iconv.py +0 -0
  139. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/misc/conv/oconv.py +0 -0
  140. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/misc/db.py +0 -0
  141. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/misc/export.py +0 -0
  142. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/misc/format.py +0 -0
  143. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/misc/mail.py +0 -0
  144. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/misc/merge.py +0 -0
  145. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/misc/pdf.py +0 -0
  146. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/misc/tests/__init__.py +0 -0
  147. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/misc/tests/test_db.py +0 -0
  148. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/misc/tests/test_fix.py +0 -0
  149. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/misc/tests/test_format.py +0 -0
  150. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/misc/tests/test_iconv.py +0 -0
  151. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/misc/tests/test_merge.py +0 -0
  152. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/misc/tests/test_oconv.py +0 -0
  153. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/misc/tests/test_original_error.py +0 -0
  154. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/misc/tests/test_timer.py +0 -0
  155. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/misc/timer.py +0 -0
  156. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/misc/tools.py +0 -0
  157. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/nosql/__init__.py +0 -0
  158. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/nosql/core/__init__.py +0 -0
  159. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/nosql/core/capabilities.py +0 -0
  160. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/nosql/core/store.py +0 -0
  161. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/nosql/core/where.py +0 -0
  162. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/nosql/exceptions.py +0 -0
  163. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/nosql/servers/__init__.py +0 -0
  164. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/nosql/servers/base/__init__.py +0 -0
  165. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/nosql/servers/cassandra/__init__.py +0 -0
  166. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/nosql/servers/cassandra/translation.py +0 -0
  167. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/nosql/servers/couchbase/__init__.py +0 -0
  168. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/nosql/servers/couchbase/translation.py +0 -0
  169. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/nosql/servers/dynamodb/__init__.py +0 -0
  170. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/nosql/servers/dynamodb/translation.py +0 -0
  171. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/nosql/servers/elasticsearch/__init__.py +0 -0
  172. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/nosql/servers/elasticsearch/translation.py +0 -0
  173. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/nosql/servers/memory/__init__.py +0 -0
  174. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/nosql/servers/mongodb/__init__.py +0 -0
  175. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/nosql/servers/mongodb/translation.py +0 -0
  176. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/nosql/servers/redis/__init__.py +0 -0
  177. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/payment/__init__.py +0 -0
  178. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/payment/authorizenet_adapter.py +0 -0
  179. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/payment/authorizenet_mirror.py +0 -0
  180. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/payment/base_adapter.py +0 -0
  181. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/payment/braintree_adapter.py +0 -0
  182. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/payment/braintree_mirror.py +0 -0
  183. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/payment/charge_rules.py +0 -0
  184. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/payment/stripe_adapter.py +0 -0
  185. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity/payment/stripe_mirror.py +0 -0
  186. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity_python.egg-info/dependency_links.txt +0 -0
  187. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity_python.egg-info/entry_points.txt +0 -0
  188. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity_python.egg-info/requires.txt +0 -0
  189. {velocity_python-0.1.94 → velocity_python-0.1.95}/src/velocity_python.egg-info/top_level.txt +0 -0
  190. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_activity_tracking_aborted_tx.py +0 -0
  191. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_amplify_build.py +0 -0
  192. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_asset_indexing.py +0 -0
  193. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_asset_references.py +0 -0
  194. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_assets_service.py +0 -0
  195. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_async_support.py +0 -0
  196. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_batch_operations.py +0 -0
  197. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_column_tx_arg.py +0 -0
  198. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_concurrency_safety.py +0 -0
  199. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_connection_pool.py +0 -0
  200. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_connection_resilience.py +0 -0
  201. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_context_job_descriptions.py +0 -0
  202. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_db_credentials_ssm_cascade.py +0 -0
  203. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_db_maintenance.py +0 -0
  204. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_decorators.py +0 -0
  205. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_delete_hook_fk_blocked.py +0 -0
  206. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_dirty_pipeline_fast_path.py +0 -0
  207. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_email_processing.py +0 -0
  208. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_enqueue_send_failures.py +0 -0
  209. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_get_cognito_user_provider.py +0 -0
  210. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_http_handler_rollback.py +0 -0
  211. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_iconv_money_to_cents.py +0 -0
  212. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_identifier_injection_guard.py +0 -0
  213. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_job_handler.py +0 -0
  214. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_job_results.py +0 -0
  215. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_json_columns.py +0 -0
  216. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_jsonb_dict_adapter.py +0 -0
  217. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_lambda_handler.py +0 -0
  218. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_lambda_handler_auth.py +0 -0
  219. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_lambda_handler_masquerade.py +0 -0
  220. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_masquerade_grant.py +0 -0
  221. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_mixins_import.py +0 -0
  222. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_n_plus_one.py +0 -0
  223. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_nosql_dynamodb.py +0 -0
  224. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_nosql_memory.py +0 -0
  225. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_nosql_mongodb.py +0 -0
  226. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_nosql_redis.py +0 -0
  227. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_nosql_translation.py +0 -0
  228. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_nosql_where.py +0 -0
  229. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_observability.py +0 -0
  230. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_on_commit.py +0 -0
  231. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_payment_authorizenet_adapter.py +0 -0
  232. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_payment_braintree_adapter.py +0 -0
  233. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_payment_braintree_mirror.py +0 -0
  234. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_payment_mirror_empty_table.py +0 -0
  235. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_payment_profile_sorting.py +0 -0
  236. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_payment_stripe_adapter.py +0 -0
  237. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_pdf.py +0 -0
  238. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_prepared_statements.py +0 -0
  239. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_psycopg3_upgrade.py +0 -0
  240. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_query_cache.py +0 -0
  241. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_restricted_direct_tables.py +0 -0
  242. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_retry_side_effect_guard.py +0 -0
  243. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_return_default_safety.py +0 -0
  244. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_row_batch_update.py +0 -0
  245. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_row_cache_staleness.py +0 -0
  246. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_schema_migrations.py +0 -0
  247. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_security_hardening.py +0 -0
  248. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_server_cursor.py +0 -0
  249. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_single_autocommit_safety.py +0 -0
  250. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_spreadsheet_functions.py +0 -0
  251. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_sqlite_backend.py +0 -0
  252. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_sqs_per_record_transactions.py +0 -0
  253. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_ssm_config.py +0 -0
  254. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_store_user_data.py +0 -0
  255. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_sys_modified_count_postgres_demo.py +0 -0
  256. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_table_alter.py +0 -0
  257. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_transaction_class_wrapping.py +0 -0
  258. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_transaction_commit_and_ownership.py +0 -0
  259. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_transaction_edge_cases.py +0 -0
  260. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_where_clause_validation.py +0 -0
  261. {velocity_python-0.1.94 → velocity_python-0.1.95}/tests/test_write_hook_create_flow.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.1.94
3
+ Version: 0.1.95
4
4
  Summary: A rapid application development library for interfacing with data storage
5
5
  Author-email: Velocity Team <info@codeclubs.org>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "velocity-python"
7
- version = "0.1.94"
7
+ version = "0.1.95"
8
8
  authors = [
9
9
  { name="Velocity Team", email="info@codeclubs.org" },
10
10
  ]
@@ -1,4 +1,4 @@
1
- __version__ = version = "0.1.94"
1
+ __version__ = version = "0.1.95"
2
2
 
3
3
  import importlib as _importlib
4
4
 
@@ -0,0 +1,237 @@
1
+ """AWS End User Messaging (two-way SMS/MMS) helpers.
2
+
3
+ Two halves, both business-agnostic:
4
+
5
+ ``parse_inbound_event(event)``
6
+ Normalize inbound two-way SMS deliveries. AWS End User Messaging publishes
7
+ each inbound message to the SNS topic configured on the receiving phone
8
+ number; in a Lambda subscribed to that topic the payload arrives as
9
+ ``event["Records"][n]["Sns"]["Message"]`` (a JSON string). This accepts a
10
+ full Lambda SNS event, a single SNS record, a decoded payload dict, or the
11
+ payload's JSON string, and returns a list of :class:`InboundSms`.
12
+
13
+ ``SmsClient``
14
+ Thin wrapper over the ``pinpoint-sms-voice-v2`` API (the SDK name for AWS
15
+ End User Messaging): ``send_text`` / ``send_media``. The boto3 client is
16
+ injectable so callers and tests never need network access.
17
+
18
+ Neither half reads configuration on its own: the origination identity (phone
19
+ number, pool ARN, or sender id) and optional configuration set are constructor
20
+ or call arguments, so applications keep ownership of where those values come
21
+ from (env, SSM, database).
22
+ """
23
+
24
+ import json
25
+ from dataclasses import dataclass, field
26
+ from typing import Any, Dict, List, Optional
27
+
28
+ from velocity.logging import get_logger
29
+
30
+ logger = get_logger(__name__)
31
+
32
+
33
+ @dataclass
34
+ class InboundSms:
35
+ """One inbound SMS/MMS message, normalized from the SNS delivery payload.
36
+
37
+ ``keyword`` is AWS's ``messageKeyword`` (the registered keyword the
38
+ carrier matched, e.g. an opt-in keyword) — it is *not* the first word of
39
+ the body; use :attr:`first_word` for command routing.
40
+ """
41
+
42
+ origination_number: str = ""
43
+ destination_number: str = ""
44
+ body: str = ""
45
+ message_id: str = ""
46
+ keyword: str = ""
47
+ media_urls: List[str] = field(default_factory=list)
48
+ raw: Dict[str, Any] = field(default_factory=dict)
49
+
50
+ @property
51
+ def first_word(self) -> str:
52
+ """First whitespace-delimited token of the body, lowercased."""
53
+ parts = (self.body or "").split()
54
+ return parts[0].lower() if parts else ""
55
+
56
+ @property
57
+ def remainder(self) -> str:
58
+ """The body with the first word removed (original casing preserved)."""
59
+ parts = (self.body or "").split(None, 1)
60
+ return parts[1] if len(parts) > 1 else ""
61
+
62
+
63
+ def _coerce_payload(value) -> Optional[Dict[str, Any]]:
64
+ """A payload dict from a dict or JSON string; None when neither."""
65
+ if isinstance(value, str):
66
+ try:
67
+ value = json.loads(value)
68
+ except (ValueError, TypeError):
69
+ return None
70
+ return value if isinstance(value, dict) else None
71
+
72
+
73
+ def _payload_to_inbound(payload: Dict[str, Any]) -> Optional[InboundSms]:
74
+ """Map one End User Messaging inbound payload onto InboundSms.
75
+
76
+ Requires at least an origination number — anything else (delivery status
77
+ events, unrelated SNS traffic on a shared topic) is not an inbound message
78
+ and returns None.
79
+ """
80
+ origination = str(payload.get("originationNumber") or "").strip()
81
+ if not origination:
82
+ return None
83
+ media = payload.get("mediaUrls")
84
+ if isinstance(media, str):
85
+ media = [media]
86
+ if not isinstance(media, list):
87
+ media = []
88
+ return InboundSms(
89
+ origination_number=origination,
90
+ destination_number=str(payload.get("destinationNumber") or "").strip(),
91
+ body=str(payload.get("messageBody") or ""),
92
+ message_id=str(payload.get("inboundMessageId") or "").strip(),
93
+ keyword=str(payload.get("messageKeyword") or "").strip(),
94
+ media_urls=[str(u) for u in media if u],
95
+ raw=payload,
96
+ )
97
+
98
+
99
+ def parse_inbound_event(event) -> List[InboundSms]:
100
+ """Normalize an inbound two-way SMS delivery into InboundSms objects.
101
+
102
+ Accepts, in order of most to least wrapped:
103
+
104
+ - a Lambda SNS event: ``{"Records": [{"Sns": {"Message": "<json>"}}]}``
105
+ - a single SNS record: ``{"Sns": {"Message": "<json>"}}``
106
+ - the decoded payload dict (or its JSON string) as published by
107
+ AWS End User Messaging
108
+
109
+ Records that do not look like inbound messages are skipped with a warning
110
+ rather than raising — one malformed record in a batch must not lose the
111
+ others.
112
+ """
113
+ if event is None:
114
+ return []
115
+ payload = _coerce_payload(event)
116
+ if payload is None:
117
+ return []
118
+
119
+ records = payload.get("Records")
120
+ if not isinstance(records, list):
121
+ # Single record or bare payload.
122
+ if "Sns" in payload:
123
+ records = [payload]
124
+ else:
125
+ inbound = _payload_to_inbound(payload)
126
+ return [inbound] if inbound else []
127
+
128
+ messages = []
129
+ for record in records:
130
+ if not isinstance(record, dict):
131
+ continue
132
+ sns = record.get("Sns")
133
+ message = _coerce_payload(sns.get("Message")) if isinstance(sns, dict) else None
134
+ inbound = _payload_to_inbound(message) if message else None
135
+ if inbound:
136
+ messages.append(inbound)
137
+ else:
138
+ logger.warning(
139
+ "parse_inbound_event: skipping record that is not an inbound "
140
+ "SMS payload",
141
+ extra={"record_keys": sorted(record.keys())},
142
+ )
143
+ return messages
144
+
145
+
146
+ class SmsClient:
147
+ """Outbound SMS/MMS via AWS End User Messaging.
148
+
149
+ ``origination_identity`` may be a phone number in E.164 form, a phone
150
+ number ARN, a pool ARN, or a sender ID — whatever the account has
151
+ provisioned. A per-call ``origination_identity`` overrides the default.
152
+ """
153
+
154
+ def __init__(
155
+ self,
156
+ origination_identity: Optional[str] = None,
157
+ configuration_set: Optional[str] = None,
158
+ client=None,
159
+ region: Optional[str] = None,
160
+ ):
161
+ self.origination_identity = origination_identity
162
+ self.configuration_set = configuration_set
163
+ self.region = region
164
+ self._client = client
165
+
166
+ @property
167
+ def client(self):
168
+ if self._client is None:
169
+ import boto3
170
+
171
+ kwargs = {"region_name": self.region} if self.region else {}
172
+ self._client = boto3.client("pinpoint-sms-voice-v2", **kwargs)
173
+ return self._client
174
+
175
+ def _resolve(self, destination, origination_identity):
176
+ destination = str(destination or "").strip()
177
+ origination = str(origination_identity or self.origination_identity or "").strip()
178
+ if not destination:
179
+ raise ValueError("SmsClient: destination phone number is required")
180
+ if not origination:
181
+ raise ValueError(
182
+ "SmsClient: origination_identity is required (constructor "
183
+ "default or per-call argument)"
184
+ )
185
+ return destination, origination
186
+
187
+ def send_text(
188
+ self,
189
+ destination: str,
190
+ body: str,
191
+ origination_identity: Optional[str] = None,
192
+ message_type: str = "TRANSACTIONAL",
193
+ configuration_set: Optional[str] = None,
194
+ context: Optional[Dict[str, str]] = None,
195
+ ) -> str:
196
+ """Send an SMS; returns the AWS message id."""
197
+ destination, origination = self._resolve(destination, origination_identity)
198
+ params = {
199
+ "DestinationPhoneNumber": destination,
200
+ "OriginationIdentity": origination,
201
+ "MessageBody": str(body or ""),
202
+ "MessageType": message_type,
203
+ }
204
+ configuration_set = configuration_set or self.configuration_set
205
+ if configuration_set:
206
+ params["ConfigurationSetName"] = configuration_set
207
+ if context:
208
+ params["Context"] = context
209
+ response = self.client.send_text_message(**params)
210
+ return response.get("MessageId", "")
211
+
212
+ def send_media(
213
+ self,
214
+ destination: str,
215
+ body: Optional[str] = None,
216
+ media_urls: Optional[List[str]] = None,
217
+ origination_identity: Optional[str] = None,
218
+ configuration_set: Optional[str] = None,
219
+ context: Optional[Dict[str, str]] = None,
220
+ ) -> str:
221
+ """Send an MMS (``media_urls`` are s3:// URIs); returns the message id."""
222
+ destination, origination = self._resolve(destination, origination_identity)
223
+ params = {
224
+ "DestinationPhoneNumber": destination,
225
+ "OriginationIdentity": origination,
226
+ }
227
+ if body:
228
+ params["MessageBody"] = str(body)
229
+ if media_urls:
230
+ params["MediaUrls"] = [str(u) for u in media_urls]
231
+ configuration_set = configuration_set or self.configuration_set
232
+ if configuration_set:
233
+ params["ConfigurationSetName"] = configuration_set
234
+ if context:
235
+ params["Context"] = context
236
+ response = self.client.send_media_message(**params)
237
+ return response.get("MessageId", "")
@@ -0,0 +1,178 @@
1
+ """Unit tests for velocity.aws.sms (AWS End User Messaging helpers)."""
2
+
3
+ import json
4
+ import unittest
5
+
6
+ from velocity.aws.sms import InboundSms, SmsClient, parse_inbound_event
7
+
8
+
9
+ def _payload(**overrides):
10
+ payload = {
11
+ "originationNumber": "+14255550182",
12
+ "destinationNumber": "+12125550101",
13
+ "messageKeyword": "KEYWORD_JOIN",
14
+ "messageBody": "receipt 42.50 office supplies",
15
+ "inboundMessageId": "cae173d2-66b9-564c-8309-21f858e9fb84",
16
+ "previousPublishedMessageId": "prev-id",
17
+ }
18
+ payload.update(overrides)
19
+ return payload
20
+
21
+
22
+ def _sns_event(*payloads):
23
+ return {
24
+ "Records": [
25
+ {
26
+ "EventSource": "aws:sns",
27
+ "Sns": {"Message": json.dumps(p)},
28
+ }
29
+ for p in payloads
30
+ ]
31
+ }
32
+
33
+
34
+ class TestParseInboundEvent(unittest.TestCase):
35
+ def test_full_lambda_sns_event(self):
36
+ event = _sns_event(_payload())
37
+ messages = parse_inbound_event(event)
38
+ self.assertEqual(len(messages), 1)
39
+ msg = messages[0]
40
+ self.assertEqual(msg.origination_number, "+14255550182")
41
+ self.assertEqual(msg.destination_number, "+12125550101")
42
+ self.assertEqual(msg.body, "receipt 42.50 office supplies")
43
+ self.assertEqual(msg.message_id, "cae173d2-66b9-564c-8309-21f858e9fb84")
44
+ self.assertEqual(msg.keyword, "KEYWORD_JOIN")
45
+ self.assertEqual(msg.media_urls, [])
46
+ self.assertEqual(msg.raw["previousPublishedMessageId"], "prev-id")
47
+
48
+ def test_multiple_records_including_mms(self):
49
+ event = _sns_event(
50
+ _payload(),
51
+ _payload(
52
+ inboundMessageId="mms-1",
53
+ mediaUrls=["s3://bucket/media/one.jpg", "s3://bucket/media/two.jpg"],
54
+ ),
55
+ )
56
+ messages = parse_inbound_event(event)
57
+ self.assertEqual(len(messages), 2)
58
+ self.assertEqual(messages[1].media_urls, [
59
+ "s3://bucket/media/one.jpg",
60
+ "s3://bucket/media/two.jpg",
61
+ ])
62
+
63
+ def test_bare_payload_dict(self):
64
+ messages = parse_inbound_event(_payload())
65
+ self.assertEqual(len(messages), 1)
66
+ self.assertEqual(messages[0].origination_number, "+14255550182")
67
+
68
+ def test_payload_json_string(self):
69
+ messages = parse_inbound_event(json.dumps(_payload()))
70
+ self.assertEqual(len(messages), 1)
71
+
72
+ def test_single_sns_record(self):
73
+ messages = parse_inbound_event({"Sns": {"Message": json.dumps(_payload())}})
74
+ self.assertEqual(len(messages), 1)
75
+
76
+ def test_malformed_record_skipped_others_kept(self):
77
+ event = _sns_event(_payload())
78
+ event["Records"].insert(0, {"Sns": {"Message": "not json"}})
79
+ event["Records"].insert(0, "not a dict")
80
+ messages = parse_inbound_event(event)
81
+ self.assertEqual(len(messages), 1)
82
+
83
+ def test_non_inbound_payload_returns_empty(self):
84
+ # A delivery-status style event has no originationNumber.
85
+ self.assertEqual(parse_inbound_event({"eventType": "TEXT_DELIVERED"}), [])
86
+ self.assertEqual(parse_inbound_event(None), [])
87
+ self.assertEqual(parse_inbound_event("not json"), [])
88
+ self.assertEqual(parse_inbound_event(42), [])
89
+
90
+ def test_media_urls_string_coerced_to_list(self):
91
+ messages = parse_inbound_event(_payload(mediaUrls="s3://bucket/only.jpg"))
92
+ self.assertEqual(messages[0].media_urls, ["s3://bucket/only.jpg"])
93
+
94
+ def test_first_word_and_remainder(self):
95
+ msg = InboundSms(body="Receipt 42.50 office supplies")
96
+ self.assertEqual(msg.first_word, "receipt")
97
+ self.assertEqual(msg.remainder, "42.50 office supplies")
98
+ self.assertEqual(InboundSms(body="").first_word, "")
99
+ self.assertEqual(InboundSms(body="help").remainder, "")
100
+
101
+
102
+ class FakeBotoClient:
103
+ def __init__(self):
104
+ self.text_calls = []
105
+ self.media_calls = []
106
+
107
+ def send_text_message(self, **kwargs):
108
+ self.text_calls.append(kwargs)
109
+ return {"MessageId": "text-msg-id"}
110
+
111
+ def send_media_message(self, **kwargs):
112
+ self.media_calls.append(kwargs)
113
+ return {"MessageId": "media-msg-id"}
114
+
115
+
116
+ class TestSmsClient(unittest.TestCase):
117
+ def setUp(self):
118
+ self.boto = FakeBotoClient()
119
+ self.client = SmsClient(
120
+ origination_identity="+18885550100",
121
+ configuration_set="default-set",
122
+ client=self.boto,
123
+ )
124
+
125
+ def test_send_text_uses_defaults(self):
126
+ message_id = self.client.send_text("+14255550182", "hello")
127
+ self.assertEqual(message_id, "text-msg-id")
128
+ call = self.boto.text_calls[0]
129
+ self.assertEqual(call["DestinationPhoneNumber"], "+14255550182")
130
+ self.assertEqual(call["OriginationIdentity"], "+18885550100")
131
+ self.assertEqual(call["MessageBody"], "hello")
132
+ self.assertEqual(call["MessageType"], "TRANSACTIONAL")
133
+ self.assertEqual(call["ConfigurationSetName"], "default-set")
134
+ self.assertNotIn("Context", call)
135
+
136
+ def test_send_text_overrides(self):
137
+ self.client.send_text(
138
+ "+14255550182",
139
+ "hello",
140
+ origination_identity="+18885550999",
141
+ configuration_set="other-set",
142
+ context={"job_id": "abc"},
143
+ )
144
+ call = self.boto.text_calls[0]
145
+ self.assertEqual(call["OriginationIdentity"], "+18885550999")
146
+ self.assertEqual(call["ConfigurationSetName"], "other-set")
147
+ self.assertEqual(call["Context"], {"job_id": "abc"})
148
+
149
+ def test_send_text_requires_destination_and_origination(self):
150
+ with self.assertRaises(ValueError):
151
+ self.client.send_text("", "hello")
152
+ bare = SmsClient(client=self.boto)
153
+ with self.assertRaises(ValueError):
154
+ bare.send_text("+14255550182", "hello")
155
+
156
+ def test_send_media(self):
157
+ message_id = self.client.send_media(
158
+ "+14255550182",
159
+ body="see attached",
160
+ media_urls=["s3://bucket/receipt.jpg"],
161
+ )
162
+ self.assertEqual(message_id, "media-msg-id")
163
+ call = self.boto.media_calls[0]
164
+ self.assertEqual(call["MediaUrls"], ["s3://bucket/receipt.jpg"])
165
+ self.assertEqual(call["MessageBody"], "see attached")
166
+
167
+ def test_send_media_body_optional(self):
168
+ self.client.send_media("+14255550182", media_urls=["s3://bucket/a.jpg"])
169
+ self.assertNotIn("MessageBody", self.boto.media_calls[0])
170
+
171
+ def test_no_configuration_set_when_unset(self):
172
+ bare = SmsClient(origination_identity="+18885550100", client=self.boto)
173
+ bare.send_text("+14255550182", "hi")
174
+ self.assertNotIn("ConfigurationSetName", self.boto.text_calls[0])
175
+
176
+
177
+ if __name__ == "__main__":
178
+ unittest.main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.1.94
3
+ Version: 0.1.95
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
@@ -9,6 +9,7 @@ src/velocity/aws/amplify_build.py
9
9
  src/velocity/aws/dirty_pipeline.py
10
10
  src/velocity/aws/job_results.py
11
11
  src/velocity/aws/s3.py
12
+ src/velocity/aws/sms.py
12
13
  src/velocity/aws/ssm_config.py
13
14
  src/velocity/aws/assets/__init__.py
14
15
  src/velocity/aws/assets/backfill.py
@@ -44,6 +45,7 @@ src/velocity/aws/tests/test_deploy_policies.py
44
45
  src/velocity/aws/tests/test_deploy_reconcile.py
45
46
  src/velocity/aws/tests/test_lambda_handler_json_serialization.py
46
47
  src/velocity/aws/tests/test_response.py
48
+ src/velocity/aws/tests/test_sms.py
47
49
  src/velocity/db/__init__.py
48
50
  src/velocity/db/exceptions.py
49
51
  src/velocity/db/maintenance.py