velocity-python 0.3.2__tar.gz → 0.4.1__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 (461) hide show
  1. {velocity_python-0.3.2 → velocity_python-0.4.1}/PKG-INFO +1 -1
  2. {velocity_python-0.3.2 → velocity_python-0.4.1}/pyproject.toml +1 -1
  3. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/__init__.py +1 -1
  4. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/authz.py +30 -0
  5. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/capabilities/__init__.py +1 -0
  6. velocity_python-0.4.1/src/velocity/aws/capabilities/workflow.py +48 -0
  7. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/mixins/__init__.py +13 -0
  8. velocity_python-0.4.1/src/velocity/aws/handlers/mixins/workflow_actions.py +491 -0
  9. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/schema.py +5 -0
  10. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/rules/__init__.py +2 -0
  11. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/rules/engine.py +8 -1
  12. velocity_python-0.4.1/src/velocity/workflow/__init__.py +163 -0
  13. velocity_python-0.4.1/src/velocity/workflow/access.py +349 -0
  14. velocity_python-0.4.1/src/velocity/workflow/contract.py +1056 -0
  15. velocity_python-0.4.1/src/velocity/workflow/definitions.py +824 -0
  16. velocity_python-0.4.1/src/velocity/workflow/engine.py +3473 -0
  17. velocity_python-0.4.1/src/velocity/workflow/hooks.py +434 -0
  18. velocity_python-0.4.1/src/velocity/workflow/notices.py +410 -0
  19. velocity_python-0.4.1/src/velocity/workflow/outbox.py +266 -0
  20. velocity_python-0.4.1/src/velocity/workflow/points.py +300 -0
  21. velocity_python-0.4.1/src/velocity/workflow/schema.py +585 -0
  22. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity_python.egg-info/PKG-INFO +1 -1
  23. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity_python.egg-info/SOURCES.txt +19 -0
  24. velocity_python-0.4.1/tests/test_workflow_actions.py +808 -0
  25. velocity_python-0.4.1/tests/test_workflow_contract.py +342 -0
  26. velocity_python-0.4.1/tests/test_workflow_definitions.py +385 -0
  27. velocity_python-0.4.1/tests/test_workflow_engine.py +1189 -0
  28. velocity_python-0.4.1/tests/test_workflow_features.py +906 -0
  29. velocity_python-0.4.1/tests/test_workflow_postgres.py +457 -0
  30. velocity_python-0.4.1/tests/test_workflow_review.py +910 -0
  31. {velocity_python-0.3.2 → velocity_python-0.4.1}/LICENSE +0 -0
  32. {velocity_python-0.3.2 → velocity_python-0.4.1}/README.md +0 -0
  33. {velocity_python-0.3.2 → velocity_python-0.4.1}/setup.cfg +0 -0
  34. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/__init__.py +0 -0
  35. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/amplify.py +0 -0
  36. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/assets/__init__.py +0 -0
  37. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/assets/backfill.py +0 -0
  38. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/assets/indexing.py +0 -0
  39. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/assets/references.py +0 -0
  40. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/assets/service.py +0 -0
  41. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/assets/usage_index.py +0 -0
  42. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/blobs.py +0 -0
  43. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/capabilities/base.py +0 -0
  44. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/capabilities/dispatch.py +0 -0
  45. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/capabilities/infra.py +0 -0
  46. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/capabilities/jobs.py +0 -0
  47. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/capabilities/provisioning.py +0 -0
  48. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/capabilities/retention.py +0 -0
  49. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/capabilities/security.py +0 -0
  50. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/capabilities/single_session.py +0 -0
  51. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/deploy/__init__.py +0 -0
  52. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/deploy/ci_gate.py +0 -0
  53. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/deploy/config.py +0 -0
  54. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/deploy/frontend_checks.py +0 -0
  55. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/deploy/operations.py +0 -0
  56. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/deploy/policies.py +0 -0
  57. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/deploy/reconcile.py +0 -0
  58. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/deploy/targets.py +0 -0
  59. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/dirty_pipeline.py +0 -0
  60. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/email.py +0 -0
  61. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/facets.py +0 -0
  62. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/failures.py +0 -0
  63. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/feature_flags.py +0 -0
  64. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/__init__.py +0 -0
  65. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/base_handler.py +0 -0
  66. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/context.py +0 -0
  67. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/context_factory.py +0 -0
  68. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/exceptions.py +0 -0
  69. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/lambda_handler.py +0 -0
  70. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/masquerade.py +0 -0
  71. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/mixins/action_names.py +0 -0
  72. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/mixins/audit_trail.py +0 -0
  73. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/mixins/client_errors.py +0 -0
  74. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/mixins/client_input.py +0 -0
  75. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/mixins/data_service.py +0 -0
  76. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/mixins/job_handler.py +0 -0
  77. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/mixins/permissions_actions.py +0 -0
  78. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/mixins/row_scoped.py +0 -0
  79. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/mixins/session_control.py +0 -0
  80. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/mixins/tiles_actions.py +0 -0
  81. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/mixins/web_handler.py +0 -0
  82. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/perf.py +0 -0
  83. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/response.py +0 -0
  84. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/handlers/sqs_handler.py +0 -0
  85. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/identity.py +0 -0
  86. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/job_lifecycle.py +0 -0
  87. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/job_results.py +0 -0
  88. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/mirror.py +0 -0
  89. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/ops.py +0 -0
  90. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/permissions.py +0 -0
  91. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/queue_defaults.py +0 -0
  92. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/s3.py +0 -0
  93. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/scheduler.py +0 -0
  94. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/settings.py +0 -0
  95. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/sms.py +0 -0
  96. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/ssm_config.py +0 -0
  97. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/tests/__init__.py +0 -0
  98. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/tests/test_base_handler_error_response.py +0 -0
  99. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/tests/test_deploy_least_privilege.py +0 -0
  100. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/tests/test_deploy_policies.py +0 -0
  101. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/tests/test_deploy_policy_size.py +0 -0
  102. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/tests/test_deploy_reconcile.py +0 -0
  103. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/tests/test_email.py +0 -0
  104. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
  105. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/tests/test_response.py +0 -0
  106. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/tests/test_sms.py +0 -0
  107. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/tiles.py +0 -0
  108. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/aws/user_data.py +0 -0
  109. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/cache.py +0 -0
  110. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/__init__.py +0 -0
  111. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/_pgddl.py +0 -0
  112. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/audit.py +0 -0
  113. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/core/__init__.py +0 -0
  114. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/core/async_support.py +0 -0
  115. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/core/column.py +0 -0
  116. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/core/database.py +0 -0
  117. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/core/decorators.py +0 -0
  118. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/core/engine.py +0 -0
  119. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/core/jsonproxy.py +0 -0
  120. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/core/lazy_engine.py +0 -0
  121. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/core/result.py +0 -0
  122. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/core/row.py +0 -0
  123. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/core/sequence.py +0 -0
  124. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/core/table.py +0 -0
  125. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/core/transaction.py +0 -0
  126. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/core/view.py +0 -0
  127. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/effective.py +0 -0
  128. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/exceptions.py +0 -0
  129. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/fragments.py +0 -0
  130. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/integrity.py +0 -0
  131. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/maintenance.py +0 -0
  132. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/migrations.py +0 -0
  133. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/__init__.py +0 -0
  134. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/base/__init__.py +0 -0
  135. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/base/initializer.py +0 -0
  136. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/base/operators.py +0 -0
  137. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/base/sql.py +0 -0
  138. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/base/types.py +0 -0
  139. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/mysql/__init__.py +0 -0
  140. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/mysql/operators.py +0 -0
  141. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/mysql/reserved.py +0 -0
  142. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/mysql/sql.py +0 -0
  143. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/mysql/types.py +0 -0
  144. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/postgres/__init__.py +0 -0
  145. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/postgres/operators.py +0 -0
  146. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/postgres/reserved.py +0 -0
  147. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/postgres/sql.py +0 -0
  148. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/postgres/types.py +0 -0
  149. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/sqlite/__init__.py +0 -0
  150. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/sqlite/operators.py +0 -0
  151. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/sqlite/reserved.py +0 -0
  152. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/sqlite/sql.py +0 -0
  153. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/sqlite/types.py +0 -0
  154. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
  155. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/sqlserver/operators.py +0 -0
  156. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
  157. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/sqlserver/sql.py +0 -0
  158. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/sqlserver/types.py +0 -0
  159. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/servers/tablehelper.py +0 -0
  160. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/__init__.py +0 -0
  161. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/common_db_test.py +0 -0
  162. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/postgres/__init__.py +0 -0
  163. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/postgres/common.py +0 -0
  164. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/postgres/conftest.py +0 -0
  165. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/postgres/test_column.py +0 -0
  166. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/postgres/test_connections.py +0 -0
  167. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/postgres/test_database.py +0 -0
  168. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/postgres/test_engine.py +0 -0
  169. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
  170. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/postgres/test_imports.py +0 -0
  171. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/postgres/test_integrity_triggers.py +0 -0
  172. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/postgres/test_result.py +0 -0
  173. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/postgres/test_row.py +0 -0
  174. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
  175. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
  176. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
  177. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
  178. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
  179. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/postgres/test_table.py +0 -0
  180. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
  181. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
  182. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/sql/__init__.py +0 -0
  183. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/sql/common.py +0 -0
  184. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
  185. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
  186. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
  187. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/test_db_utils.py +0 -0
  188. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/test_postgres.py +0 -0
  189. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
  190. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
  191. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/test_result_caching.py +0 -0
  192. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
  193. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
  194. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
  195. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
  196. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/test_sql_builder.py +0 -0
  197. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/test_tablehelper.py +0 -0
  198. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tests/test_view_helper.py +0 -0
  199. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/tunnel.py +0 -0
  200. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/db/utils.py +0 -0
  201. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/hooks.py +0 -0
  202. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/logging.py +0 -0
  203. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/__init__.py +0 -0
  204. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/conv/__init__.py +0 -0
  205. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/conv/iconv.py +0 -0
  206. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/conv/oconv.py +0 -0
  207. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/db.py +0 -0
  208. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/export.py +0 -0
  209. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/format.py +0 -0
  210. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/mail.py +0 -0
  211. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/merge.py +0 -0
  212. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/pdf.py +0 -0
  213. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/tests/__init__.py +0 -0
  214. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/tests/test_db.py +0 -0
  215. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/tests/test_fix.py +0 -0
  216. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/tests/test_format.py +0 -0
  217. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/tests/test_iconv.py +0 -0
  218. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/tests/test_mail_send.py +0 -0
  219. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/tests/test_merge.py +0 -0
  220. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/tests/test_oconv.py +0 -0
  221. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/tests/test_original_error.py +0 -0
  222. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/tests/test_timer.py +0 -0
  223. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/timer.py +0 -0
  224. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/misc/tools.py +0 -0
  225. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/nosql/__init__.py +0 -0
  226. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/nosql/core/__init__.py +0 -0
  227. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/nosql/core/capabilities.py +0 -0
  228. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/nosql/core/store.py +0 -0
  229. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/nosql/core/where.py +0 -0
  230. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/nosql/exceptions.py +0 -0
  231. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/nosql/servers/__init__.py +0 -0
  232. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/nosql/servers/base/__init__.py +0 -0
  233. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/nosql/servers/cassandra/__init__.py +0 -0
  234. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/nosql/servers/cassandra/translation.py +0 -0
  235. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/nosql/servers/couchbase/__init__.py +0 -0
  236. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/nosql/servers/couchbase/translation.py +0 -0
  237. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/nosql/servers/dynamodb/__init__.py +0 -0
  238. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/nosql/servers/dynamodb/translation.py +0 -0
  239. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/nosql/servers/elasticsearch/__init__.py +0 -0
  240. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/nosql/servers/elasticsearch/translation.py +0 -0
  241. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/nosql/servers/memory/__init__.py +0 -0
  242. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/nosql/servers/mongodb/__init__.py +0 -0
  243. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/nosql/servers/mongodb/translation.py +0 -0
  244. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/nosql/servers/redis/__init__.py +0 -0
  245. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/payment/__init__.py +0 -0
  246. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/payment/_mirror_core.py +0 -0
  247. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/payment/authorizenet_adapter.py +0 -0
  248. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/payment/authorizenet_mirror.py +0 -0
  249. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/payment/base_adapter.py +0 -0
  250. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/payment/braintree_adapter.py +0 -0
  251. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/payment/braintree_mirror.py +0 -0
  252. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/payment/charge_rules.py +0 -0
  253. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/payment/money.py +0 -0
  254. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/payment/plaid_api.py +0 -0
  255. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/payment/plaid_webhook.py +0 -0
  256. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/payment/redact.py +0 -0
  257. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/payment/schema.py +0 -0
  258. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/payment/stripe_adapter.py +0 -0
  259. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/payment/stripe_mirror.py +0 -0
  260. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/rules/expressions.py +0 -0
  261. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/rules/facts.py +0 -0
  262. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/rules/storage.py +0 -0
  263. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/testing/__init__.py +0 -0
  264. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity/testing/fake_db.py +0 -0
  265. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity_python.egg-info/dependency_links.txt +0 -0
  266. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity_python.egg-info/entry_points.txt +0 -0
  267. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity_python.egg-info/requires.txt +0 -0
  268. {velocity_python-0.3.2 → velocity_python-0.4.1}/src/velocity_python.egg-info/top_level.txt +0 -0
  269. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_access_error_403.py +0 -0
  270. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_action_names.py +0 -0
  271. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_activity_tracking_aborted_tx.py +0 -0
  272. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_amplify_redirects.py +0 -0
  273. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_asset_indexing.py +0 -0
  274. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_asset_references.py +0 -0
  275. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_assets_service.py +0 -0
  276. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_async_support.py +0 -0
  277. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_audit_log_confidential.py +0 -0
  278. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_audit_mixin.py +0 -0
  279. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_audit_registry_table_gate.py +0 -0
  280. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_authz.py +0 -0
  281. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_aws_package_hygiene.py +0 -0
  282. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_batch_operations.py +0 -0
  283. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_blob_store.py +0 -0
  284. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_business_objects.py +0 -0
  285. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_capabilities.py +0 -0
  286. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_capability_catalog.py +0 -0
  287. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_capability_dependencies.py +0 -0
  288. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_check_changelog.py +0 -0
  289. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_ci_gate.py +0 -0
  290. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_client_errors_mixin.py +0 -0
  291. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_client_input_contract_export.py +0 -0
  292. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_client_sql_boundary.py +0 -0
  293. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_client_table_gate_case_and_pointers.py +0 -0
  294. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_cognito_client_lazy.py +0 -0
  295. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_cognito_user_cache.py +0 -0
  296. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_column_tx_arg.py +0 -0
  297. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_concurrency_safety.py +0 -0
  298. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_connection_pool.py +0 -0
  299. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_connection_resilience.py +0 -0
  300. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_context_enqueue_idempotency.py +0 -0
  301. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_context_flags_loader.py +0 -0
  302. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_context_job_descriptions.py +0 -0
  303. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_context_queue_job.py +0 -0
  304. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_conv_combine.py +0 -0
  305. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_data_service_owner_scope.py +0 -0
  306. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_data_service_query_cap.py +0 -0
  307. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_data_service_renumber.py +0 -0
  308. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_data_service_row_number.py +0 -0
  309. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_data_service_save_as.py +0 -0
  310. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_data_service_security_gates.py +0 -0
  311. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_db_audit_required.py +0 -0
  312. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_db_credentials_ssm_cascade.py +0 -0
  313. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_db_effective.py +0 -0
  314. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_db_integrity.py +0 -0
  315. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_db_maintenance.py +0 -0
  316. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_db_tunnel.py +0 -0
  317. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_declared_column_types.py +0 -0
  318. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_decorators.py +0 -0
  319. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_delete_hook_fk_blocked.py +0 -0
  320. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_deploy_config_fields.py +0 -0
  321. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_deploy_engine.py +0 -0
  322. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_deploy_frontend_checks.py +0 -0
  323. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_deploy_operations_queue.py +0 -0
  324. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_deploy_policies.py +0 -0
  325. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_dirty_pipeline_consumer.py +0 -0
  326. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_dirty_pipeline_fast_path.py +0 -0
  327. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_dlq_constant.py +0 -0
  328. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_docs_snippets.py +0 -0
  329. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_email_processing.py +0 -0
  330. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_enqueue_metric_namespace.py +0 -0
  331. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_enqueue_send_failures.py +0 -0
  332. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_ensure_infrastructure.py +0 -0
  333. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_export_workbook.py +0 -0
  334. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_facets.py +0 -0
  335. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_facets_and_where.py +0 -0
  336. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_failures.py +0 -0
  337. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_feature_flags.py +0 -0
  338. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_get_cognito_user_provider.py +0 -0
  339. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_hooks.py +0 -0
  340. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_http_handler_rollback.py +0 -0
  341. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_iconv_money_to_cents.py +0 -0
  342. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_identifier_injection_guard.py +0 -0
  343. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_identity.py +0 -0
  344. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_job_handler.py +0 -0
  345. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_job_lifecycle.py +0 -0
  346. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_job_record_guard.py +0 -0
  347. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_job_record_guard_postgres.py +0 -0
  348. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_job_results.py +0 -0
  349. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_json_columns.py +0 -0
  350. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_jsonb_dict_adapter.py +0 -0
  351. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_lambda_handler.py +0 -0
  352. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_lambda_handler_auth.py +0 -0
  353. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_lambda_handler_masquerade.py +0 -0
  354. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_lambda_handler_user_cache.py +0 -0
  355. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_lazy_engine.py +0 -0
  356. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_masquerade_grant.py +0 -0
  357. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_merge_on_conflict.py +0 -0
  358. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_mirror.py +0 -0
  359. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_mirror_upsert_public.py +0 -0
  360. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_mixins_import.py +0 -0
  361. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_n_plus_one.py +0 -0
  362. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_nosql_dynamodb.py +0 -0
  363. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_nosql_memory.py +0 -0
  364. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_nosql_mongodb.py +0 -0
  365. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_nosql_redis.py +0 -0
  366. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_nosql_translation.py +0 -0
  367. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_nosql_where.py +0 -0
  368. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_observability.py +0 -0
  369. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_on_action_default.py +0 -0
  370. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_on_commit.py +0 -0
  371. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_ops.py +0 -0
  372. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_payment_audit_fixes.py +0 -0
  373. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_payment_authorizenet_adapter.py +0 -0
  374. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_payment_braintree_adapter.py +0 -0
  375. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_payment_braintree_mirror.py +0 -0
  376. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_payment_business_agnostic.py +0 -0
  377. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_payment_charge_reference.py +0 -0
  378. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_payment_extra_bounds.py +0 -0
  379. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_payment_lazy_imports.py +0 -0
  380. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_payment_mirror_empty_table.py +0 -0
  381. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_payment_profile_sorting.py +0 -0
  382. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_payment_stripe_adapter.py +0 -0
  383. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_pdf.py +0 -0
  384. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_permissions.py +0 -0
  385. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_permissions_actions.py +0 -0
  386. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_permissions_notices.py +0 -0
  387. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_permissions_review.py +0 -0
  388. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_pgserver_bulk_cache.py +0 -0
  389. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_pgserver_capability_types.py +0 -0
  390. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_pgserver_ensure_infrastructure.py +0 -0
  391. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_pgserver_get_tables.py +0 -0
  392. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_pgserver_permissions.py +0 -0
  393. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_pgserver_pointer_joins.py +0 -0
  394. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_pgserver_row_scope.py +0 -0
  395. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_pgserver_search_export.py +0 -0
  396. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_pgserver_settings.py +0 -0
  397. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_pgserver_tiles.py +0 -0
  398. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_pgserver_user_data.py +0 -0
  399. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_plaid_api.py +0 -0
  400. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_plaid_webhook.py +0 -0
  401. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_pointer_join_sql.py +0 -0
  402. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_postgres_initializer_config.py +0 -0
  403. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_postgres_py_type.py +0 -0
  404. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_prepared_statements.py +0 -0
  405. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_psycopg3_upgrade.py +0 -0
  406. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_query_cache.py +0 -0
  407. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_query_missing_table_unlocked.py +0 -0
  408. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_raw_sql_prefix_policy.py +0 -0
  409. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_read_cache.py +0 -0
  410. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_readme_imports.py +0 -0
  411. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_record_event_postgres.py +0 -0
  412. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_record_event_transaction.py +0 -0
  413. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_response.py +0 -0
  414. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_restricted_direct_tables.py +0 -0
  415. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_result_returning.py +0 -0
  416. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_retry_side_effect_guard.py +0 -0
  417. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_return_default_safety.py +0 -0
  418. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_row_batch_update.py +0 -0
  419. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_row_cache_staleness.py +0 -0
  420. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_row_scoped_data_service.py +0 -0
  421. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_rules.py +0 -0
  422. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_rules_storage.py +0 -0
  423. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_s3_bucket_config.py +0 -0
  424. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_s3_list_client_files.py +0 -0
  425. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_s3_upload.py +0 -0
  426. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_scheduler.py +0 -0
  427. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_schema_lock_config_other_backends.py +0 -0
  428. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_schema_migrations.py +0 -0
  429. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_search_paging_export.py +0 -0
  430. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_security_capability.py +0 -0
  431. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_security_events_table_gate.py +0 -0
  432. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_security_hardening.py +0 -0
  433. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_server_cursor.py +0 -0
  434. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_session_control.py +0 -0
  435. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_settings.py +0 -0
  436. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_single_autocommit_safety.py +0 -0
  437. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_single_session_capability.py +0 -0
  438. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_sms_twilio_signature.py +0 -0
  439. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_spreadsheet_functions.py +0 -0
  440. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_sqlite_backend.py +0 -0
  441. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_sqs_per_record_transactions.py +0 -0
  442. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_ssm_config.py +0 -0
  443. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_ssm_config_require.py +0 -0
  444. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_store_user_data.py +0 -0
  445. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_sys_modified_count_postgres_demo.py +0 -0
  446. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_table_alter.py +0 -0
  447. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_table_claim.py +0 -0
  448. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_table_find_cache.py +0 -0
  449. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_table_purge.py +0 -0
  450. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_testing_fake_db.py +0 -0
  451. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_tiles.py +0 -0
  452. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_transaction_class_wrapping.py +0 -0
  453. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_transaction_commit_and_ownership.py +0 -0
  454. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_transaction_edge_cases.py +0 -0
  455. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_tx_lifecycle_retry.py +0 -0
  456. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_tx_read_cache_and_bulk.py +0 -0
  457. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_user_data.py +0 -0
  458. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_user_not_provisioned.py +0 -0
  459. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_where_clause_validation.py +0 -0
  460. {velocity_python-0.3.2 → velocity_python-0.4.1}/tests/test_write_hook_create_flow.py +0 -0
  461. {velocity_python-0.3.2 → velocity_python-0.4.1}/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.3.2
3
+ Version: 0.4.1
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.3.2"
7
+ version = "0.4.1"
8
8
  authors = [
9
9
  { name="Velocity Team", email="info@codeclubs.org" },
10
10
  ]
@@ -1,4 +1,4 @@
1
- __version__ = version = "0.3.2"
1
+ __version__ = version = "0.4.1"
2
2
 
3
3
  import importlib as _importlib
4
4
 
@@ -961,6 +961,36 @@ SECURITY_EVENTS_TABLE = "sys_security_events"
961
961
  protect_tables(SECURITY_EVENTS_TABLE)
962
962
  confidential_tables(SECURITY_EVENTS_TABLE)
963
963
 
964
+ # velocity.workflow's tables, registered here for the same reason: the generic
965
+ # Data actions must refuse them whether or not anything imported
966
+ # velocity.workflow first. Route definitions change only through the
967
+ # workflow's own functions; dockets are read only through its contract
968
+ # builder (a generic read would bypass the visibility rule and expose, for
969
+ # one, every wait's correlation token). velocity.workflow.schema checks that
970
+ # it declares exactly these.
971
+ WORKFLOW_DEFINITION_TABLES = (
972
+ "sys_route",
973
+ "sys_route_station",
974
+ "sys_route_pass",
975
+ "sys_route_test",
976
+ )
977
+ WORKFLOW_DOCKET_TABLES = (
978
+ "sys_docket",
979
+ "sys_docket_position",
980
+ "sys_docket_document",
981
+ "sys_docket_mark",
982
+ "sys_docket_hold",
983
+ "sys_docket_flag",
984
+ "sys_docket_wait",
985
+ "sys_docket_link",
986
+ "sys_docket_outbox",
987
+ "sys_docket_seen",
988
+ "sys_notice",
989
+ "sys_tray_handover",
990
+ )
991
+ protect_tables(*WORKFLOW_DEFINITION_TABLES, *WORKFLOW_DOCKET_TABLES)
992
+ confidential_tables(*WORKFLOW_DOCKET_TABLES)
993
+
964
994
 
965
995
  # ── the action index ───────────────────────────────────────────────────────
966
996
 
@@ -46,6 +46,7 @@ from . import security # noqa: E402,F401
46
46
  from . import infra # noqa: E402,F401
47
47
  from . import single_session # noqa: E402,F401
48
48
  from . import jobs # noqa: E402,F401
49
+ from . import workflow # noqa: E402,F401
49
50
  from .security import SecurityMixin # noqa: E402,F401
50
51
 
51
52
  __all__ = [
@@ -0,0 +1,48 @@
1
+ """workflow: the timers and the outbox of :mod:`velocity.workflow`, on a schedule.
2
+
3
+ Dockets keep time through this capability: once a minute ``sweep`` marks SLA
4
+ breaches (they stay), missed waits, expired claims and due hand-overs
5
+ (:func:`velocity.workflow.sweep`), then drains the outbox (program jobs,
6
+ notice emails; :func:`velocity.workflow.outbox.drain`). ``drain`` alone is
7
+ also schedulable.
8
+
9
+ Schema: the tables are the ``workflow`` infrastructure group
10
+ (``ensure_infrastructure(tx, include=("workflow",))``); the parts a column
11
+ list cannot declare — append-only, audit-required marks and the possession
12
+ constraint — come from :func:`velocity.workflow.ensure_schema`, which the
13
+ app's schema lane runs (it does both).
14
+
15
+ Handler mixins: ``WorkflowMixin`` on the Data handler (client actions),
16
+ ``WorkflowQueueMixin`` on the queue handler.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from .base import Capability, capability
22
+
23
+ EVERY_MINUTE = "* * * * *"
24
+
25
+
26
+ @capability
27
+ class Workflow(Capability):
28
+ name = "workflow"
29
+ label = "Workflow"
30
+ description = (
31
+ "Dockets moving along routes of stations: SLA breaches, missed waits, "
32
+ "expired claims and hand-overs once a minute, and the outbox (program "
33
+ "jobs and notice emails) drained. No per-site policy."
34
+ )
35
+ schedules = (("sweep", EVERY_MINUTE),)
36
+ mixins = (
37
+ "velocity.aws.handlers.mixins.workflow_actions:WorkflowMixin",
38
+ "velocity.aws.handlers.mixins.workflow_actions:WorkflowQueueMixin",
39
+ )
40
+
41
+ def handle(self, action, tx, context, config):
42
+ from velocity.workflow import engine, outbox
43
+
44
+ if action == "sweep":
45
+ return {"swept": engine.sweep(tx), "drained": outbox.drain(tx, context)}
46
+ if action == "drain":
47
+ return {"drained": outbox.drain(tx, context)}
48
+ raise ValueError(f"workflow: unknown action {action!r}")
@@ -17,6 +17,9 @@ This package provides mixins for common Lambda handler patterns:
17
17
  reportClientError / ErrorBoundary) to velocity.aws.failures
18
18
  - DirtyPipelineMixin: the process-dirty-object queue consumer and the
19
19
  sweep-dirty-rows action (velocity.aws.dirty_pipeline)
20
+ - WorkflowMixin / WorkflowQueueMixin: the workflow-* actions over dockets
21
+ (velocity.workflow): trays, the docket contract, acts, claims, flags,
22
+ notices; and the queue side (program jobs, outbox drain, timers)
20
23
  - SchedulerMixin: run-schedules, the once-a-minute tick over sys_schedules
21
24
  (velocity.aws.scheduler)
22
25
  """
@@ -30,6 +33,12 @@ from .session_control import SessionControlMixin
30
33
  from .action_names import action_names
31
34
  from .permissions_actions import PERMISSION_ACTIONS, PermissionsMixin
32
35
  from .tiles_actions import TILE_ACTIONS, TilesMixin
36
+ from .workflow_actions import (
37
+ WORKFLOW_ACTIONS,
38
+ WORKFLOW_QUEUE_ACTIONS,
39
+ WorkflowMixin,
40
+ WorkflowQueueMixin,
41
+ )
33
42
  from .client_errors import ClientErrorReporting, AnonymousClientErrorReporting
34
43
  from velocity.aws.dirty_pipeline import DirtyPipelineMixin
35
44
  from velocity.aws.scheduler import SchedulerMixin
@@ -47,6 +56,10 @@ __all__ = [
47
56
  "PERMISSION_ACTIONS",
48
57
  "TilesMixin",
49
58
  "TILE_ACTIONS",
59
+ "WorkflowMixin",
60
+ "WorkflowQueueMixin",
61
+ "WORKFLOW_ACTIONS",
62
+ "WORKFLOW_QUEUE_ACTIONS",
50
63
  "action_names",
51
64
  "ClientErrorReporting",
52
65
  "AnonymousClientErrorReporting",
@@ -0,0 +1,491 @@
1
+ """The actions behind the workflow screens (``velocity.workflow``).
2
+
3
+ A site adds :class:`WorkflowMixin` to its Data handler and lists the actions
4
+ in ``client_actions`` (``client_actions = MINE | WORKFLOW_ACTIONS``)::
5
+
6
+ workflow-tray {view, station?, cursor?, limit?} -> {counts, rows, cursor?, as_of}
7
+ workflow-docket {docket} -> Docket
8
+ workflow-act {docket, action, inputs?, comment?, request_key}
9
+ -> {ok, docket} | 4xx {ok: false, reasons}
10
+ workflow-flag {docket, note?} | {docket, clear} -> {ok, docket}
11
+ workflow-claim {docket, position, release?} -> {ok, docket}
12
+ workflow-notices {unread_only?, cursor?, limit?} -> {unread, rows, cursor?}
13
+ workflow-notice-ack {notice} | {all: true} -> {ok, unread}
14
+
15
+ and :class:`WorkflowQueueMixin` to its queue handler (never reachable over
16
+ HTTP): ``workflow-program`` (a program station's job), ``workflow-drain``
17
+ (the outbox) and ``workflow-sweep`` (the timers).
18
+
19
+ **Authorization.** Every action is :func:`velocity.aws.authz.require` on a
20
+ named action (:mod:`velocity.workflow.access`) with the docket as a resource
21
+ whose relations come from the person's seats — the site's
22
+ ``workflow_seats`` (default: :func:`velocity.workflow.register_seats`'s
23
+ hook), never from the request. An act or claim allowed only by
24
+ ``workflow.oversee`` or ``workflow.act_any`` is a privileged use: authz
25
+ records it before it happens, and the engine then acts with ``override``.
26
+
27
+ **Refusals** are ``{ok: false, reasons: [Reason]}`` with HTTP 403 (not
28
+ yours) or 409 (the docket's state or a guard said no), or 400 for a
29
+ malformed request. A docket the person may not see is refused exactly like
30
+ one that does not exist.
31
+ """
32
+
33
+ from __future__ import annotations
34
+
35
+ import dataclasses
36
+ import logging
37
+
38
+ from velocity.aws import authz
39
+ from velocity.aws.handlers.exceptions import AccessError
40
+ from .action_names import action_names
41
+
42
+ logger = logging.getLogger(__name__)
43
+
44
+ FORBIDDEN = 403
45
+ CONFLICT = 409
46
+ BAD_REQUEST = 400
47
+
48
+
49
+ def _workflow():
50
+ # imported lazily: velocity.workflow pulls in the rules package
51
+ from velocity import workflow
52
+
53
+ return workflow
54
+
55
+
56
+ class _Refusal(Exception):
57
+ def __init__(self, status, reasons):
58
+ super().__init__(status)
59
+ self.status = status
60
+ self.reasons = reasons
61
+
62
+
63
+ def _reasons_status(reasons) -> int:
64
+ codes = {r.get("code") for r in reasons or ()}
65
+ return (
66
+ FORBIDDEN if codes & {"not_available", "not_yours", "forbidden"} else CONFLICT
67
+ )
68
+
69
+
70
+ class WorkflowMixin:
71
+ """See the module docstring."""
72
+
73
+ workflow_tray_limit = 50
74
+
75
+ # ── seams ──────────────────────────────────────────────────────────────
76
+
77
+ def workflow_seats(self, tx, context, principal):
78
+ """Where the signed-in person sits (override, or register a seats
79
+ hook). Seats without a ``person`` get the user row id."""
80
+ return _workflow().hooks.seats_for(tx, principal)
81
+
82
+ # ── helpers ────────────────────────────────────────────────────────────
83
+
84
+ def _wf_principal(self, context):
85
+ principal = authz.principal_for(self, context)
86
+ if principal.user_sys_id is None:
87
+ raise AccessError(authz.REFUSED)
88
+ return principal
89
+
90
+ def _wf_seats(self, tx, context, principal):
91
+ seats = self.workflow_seats(tx, context, principal)
92
+ wf = _workflow()
93
+ if seats is None:
94
+ seats = wf.NO_SEATS
95
+ if not isinstance(seats, wf.Seats):
96
+ raise AccessError(authz.REFUSED)
97
+ if not seats.person:
98
+ seats = dataclasses.replace(seats, person=str(principal.user_sys_id))
99
+ return seats
100
+
101
+ def _wf_docket(self, tx, payload, key="docket"):
102
+ wf = _workflow()
103
+ try:
104
+ docket = wf.engine.docket_row(tx, (payload or {}).get(key))
105
+ except wf.WorkflowError:
106
+ # never say whether a docket the person can't see exists
107
+ raise AccessError(authz.REFUSED) from None
108
+ route = wf.engine._Ctx(tx).route(docket["route_id"])
109
+ return docket, route
110
+
111
+ def _wf_require(self, tx, context, action, docket, relations):
112
+ """``authz.require`` on a docket-scoped action. Every refusal says the
113
+ same sentence (``authz.REFUSED``): not which permission was missing,
114
+ and not whether the docket exists."""
115
+ wf = _workflow()
116
+ try:
117
+ return authz.require(
118
+ self, tx, context, action, wf.access.resource(docket, relations)
119
+ )
120
+ except AccessError:
121
+ raise AccessError(authz.REFUSED) from None
122
+
123
+ def _wf_masquerading(self, principal) -> bool:
124
+ return principal.real_user is not None
125
+
126
+ def _wf_decider(self, context):
127
+ """The viewer's ``may_view``: the site's authz decision on
128
+ ``workflow.docket.view`` for one docket (a forbid rule hides it from
129
+ trays and links too). A privileged permit (``view_any``) is not
130
+ used here: listing is never a recorded use."""
131
+ wf = _workflow()
132
+
133
+ def decide(docket, relations):
134
+ decision = authz.decide(
135
+ self, wf.access.VIEW, wf.access.resource(docket, relations), context
136
+ )
137
+ return decision.allowed and not decision.audit
138
+
139
+ return decide
140
+
141
+ def _wf_allows(self, tx, context, *, record=True):
142
+ """The actor's write decisions: ``authz.require`` on the named action
143
+ (``workflow.sheet.write`` / ``workflow.hold.release``) with the
144
+ docket's entity scope and the permission it needs, so scoped grants,
145
+ denials, forbids (and masquerade) apply. A refusal answers False."""
146
+ wf = _workflow()
147
+
148
+ def allows(action, permission, docket):
149
+ if not record:
150
+ # for display only (the action list): never a recorded use
151
+ decision = authz.decide(
152
+ self,
153
+ action,
154
+ wf.access.resource(docket, (), permission=permission),
155
+ context,
156
+ )
157
+ return decision.allowed and not decision.audit
158
+ try:
159
+ authz.require(
160
+ self,
161
+ tx,
162
+ context,
163
+ action,
164
+ wf.access.resource(docket, (), permission=permission),
165
+ )
166
+ except AccessError:
167
+ return False
168
+ return True
169
+
170
+ return allows
171
+
172
+ def _wf_actor(self, seats, decision, principal, tx=None, context=None):
173
+ """The actor a decision allows: ``override`` for a route owner or
174
+ ``workflow.act_any`` (both recorded by authz), ``cross_entity`` only
175
+ for ``workflow.act_any``."""
176
+ wf = _workflow()
177
+ return wf.engine.Actor.person(
178
+ seats,
179
+ override=decision.rule in wf.access.OVERRIDE_RULES,
180
+ cross_entity=decision.rule == "workflow.act_any",
181
+ allows=self._wf_allows(tx, context) if tx is not None else None,
182
+ )
183
+
184
+ def _wf_viewer(self, tx, context, docket, route, principal, seats):
185
+ """Require ``workflow.docket.view`` and build the viewer; whether an
186
+ override would let them act is asked (not recorded) for the action
187
+ list."""
188
+ wf = _workflow()
189
+ relations = wf.access.view_relations(tx, docket, route, seats)
190
+ self._wf_require(tx, context, wf.access.VIEW, docket, relations)
191
+ probe = authz.decide(
192
+ self,
193
+ wf.access.ACT,
194
+ wf.access.resource(docket, relations & {wf.access.OVERSEES}),
195
+ context,
196
+ )
197
+ override = probe.allowed and probe.rule in wf.access.OVERRIDE_RULES
198
+ return wf.contract.Viewer(
199
+ seats=seats,
200
+ principal=principal,
201
+ act_override=override,
202
+ cross_entity=override and probe.rule == "workflow.act_any",
203
+ view_decider=self._wf_decider(context),
204
+ allows=self._wf_allows(tx, context, record=False),
205
+ )
206
+
207
+ def _wf_refuse(self, context, status, reasons):
208
+ context.response().set_status(status)
209
+ context.response().set_body({"ok": False, "reasons": list(reasons)})
210
+
211
+ def _wf_after_change(self, tx, context, docket_id):
212
+ """Ask for the outbox to be drained once this transaction commits."""
213
+ wf = _workflow()
214
+ if not wf.outbox.has_pending(tx, docket_id):
215
+ return
216
+ try:
217
+ context.enqueue(wf.outbox.DRAIN_ACTION, {}, suppress_job_activity=True)
218
+ except Exception:
219
+ # the scheduled drain picks it up within a minute
220
+ logger.warning("workflow drain enqueue failed", exc_info=True)
221
+
222
+ def _wf_guarded(self, tx, context, work):
223
+ """Run ``work`` in a savepoint; a refusal rolls back everything it
224
+ wrote and answers ``{ok: false, reasons}`` with its status."""
225
+ wf = _workflow()
226
+ try:
227
+ with tx.savepoint():
228
+ return work()
229
+ except AccessError:
230
+ self._wf_refuse(
231
+ context, FORBIDDEN, [{"code": "forbidden", "text": authz.REFUSED}]
232
+ )
233
+ except _Refusal as exc:
234
+ self._wf_refuse(context, exc.status, exc.reasons)
235
+ except wf.WorkflowRefused as exc:
236
+ status = exc.status if exc.status in (400, 403, 409) else CONFLICT
237
+ self._wf_refuse(context, status, exc.reasons)
238
+ except wf.WorkflowError as exc:
239
+ self._wf_refuse(
240
+ context, BAD_REQUEST, [{"code": "invalid", "text": str(exc)}]
241
+ )
242
+ return None
243
+
244
+ def _wf_docket_body(self, tx, context, docket_id, principal, seats):
245
+ wf = _workflow()
246
+ docket, route = self._wf_docket(tx, {"docket": docket_id})
247
+ viewer = self._wf_viewer(tx, context, docket, route, principal, seats)
248
+ # under masquerade nothing is written, not even "seen"
249
+ return wf.contract.build_docket(
250
+ tx,
251
+ docket["sys_id"],
252
+ viewer,
253
+ record_seen=not self._wf_masquerading(principal),
254
+ )
255
+
256
+ # ── reading ────────────────────────────────────────────────────────────
257
+
258
+ def OnActionWorkflowTray(self, tx, context):
259
+ wf = _workflow()
260
+ payload = context.payload() or {}
261
+ principal = self._wf_principal(context)
262
+ authz.require(self, tx, context, wf.access.TRAY)
263
+ seats = self._wf_seats(tx, context, principal)
264
+ viewer = wf.contract.Viewer(
265
+ seats=seats, principal=principal, view_decider=self._wf_decider(context)
266
+ )
267
+
268
+ def work():
269
+ body = wf.contract.tray(
270
+ tx,
271
+ viewer,
272
+ payload.get("view") or "mine",
273
+ station=payload.get("station"),
274
+ cursor=payload.get("cursor"),
275
+ limit=payload.get("limit") or self.workflow_tray_limit,
276
+ counts=payload.get("counts") is not False,
277
+ )
278
+ context.response().set_body(body)
279
+
280
+ self._wf_guarded(tx, context, work)
281
+
282
+ def OnActionWorkflowDocket(self, tx, context):
283
+ payload = context.payload() or {}
284
+ principal = self._wf_principal(context)
285
+ seats = self._wf_seats(tx, context, principal)
286
+ body = self._wf_docket_body(
287
+ tx, context, payload.get("docket"), principal, seats
288
+ )
289
+ context.response().set_body(body)
290
+
291
+ # ── acting ─────────────────────────────────────────────────────────────
292
+
293
+ def OnActionWorkflowAct(self, tx, context):
294
+ wf = _workflow()
295
+ payload = context.payload() or {}
296
+ principal = self._wf_principal(context)
297
+ seats = self._wf_seats(tx, context, principal)
298
+
299
+ def work():
300
+ action = payload.get("action")
301
+ # a malformed action id is a bad request, before anything is
302
+ # looked up (it names no target to authorize)
303
+ wf.engine.parse_action(action)
304
+ docket, route = self._wf_docket(tx, payload)
305
+ request_key = payload.get("request_key")
306
+ if isinstance(request_key, str) and wf.engine.already_acted(
307
+ tx, docket["sys_id"], wf.engine.Actor.person(seats), request_key
308
+ ):
309
+ # a retry of this person's own request that succeeded: answer
310
+ # with the docket as it is (seeing it is still checked)
311
+ body = self._wf_docket_body(
312
+ tx, context, docket["sys_id"], principal, seats
313
+ )
314
+ context.response().set_body({"ok": True, "docket": body})
315
+ return
316
+ relations = wf.access.act_relations(tx, docket, route, seats, action)
317
+ decision = self._wf_require(tx, context, wf.access.ACT, docket, relations)
318
+ # the rest of the payload is checked only once the act is allowed
319
+ if not isinstance(request_key, str) or not request_key.strip():
320
+ raise _Refusal(
321
+ BAD_REQUEST,
322
+ [{"code": "request_key", "text": "A request key is required."}],
323
+ )
324
+ inputs = payload.get("inputs")
325
+ if inputs is not None and not isinstance(inputs, dict):
326
+ raise _Refusal(
327
+ BAD_REQUEST,
328
+ [{"code": "inputs", "text": "inputs must be an object."}],
329
+ )
330
+ comment = payload.get("comment")
331
+ if comment is not None and not isinstance(comment, str):
332
+ raise _Refusal(
333
+ BAD_REQUEST, [{"code": "comment", "text": "comment must be text."}]
334
+ )
335
+ result = wf.engine.act(
336
+ tx,
337
+ docket["sys_id"],
338
+ action,
339
+ inputs,
340
+ self._wf_actor(seats, decision, principal, tx, context),
341
+ request_key,
342
+ comment=comment,
343
+ )
344
+ if not result.ok:
345
+ raise _Refusal(_reasons_status(result.reasons), result.reasons)
346
+ self._wf_after_change(tx, context, docket["sys_id"])
347
+ body = self._wf_docket_body(tx, context, docket["sys_id"], principal, seats)
348
+ context.response().set_body({"ok": True, "docket": body})
349
+
350
+ self._wf_guarded(tx, context, work)
351
+
352
+ def OnActionWorkflowClaim(self, tx, context):
353
+ wf = _workflow()
354
+ payload = context.payload() or {}
355
+ principal = self._wf_principal(context)
356
+ seats = self._wf_seats(tx, context, principal)
357
+
358
+ def work():
359
+ docket, route = self._wf_docket(tx, payload)
360
+ try:
361
+ position = wf.engine._position(tx, payload.get("position"))
362
+ except wf.WorkflowError:
363
+ raise AccessError(authz.REFUSED) from None
364
+ if position["docket_id"] != docket["sys_id"]:
365
+ raise AccessError(authz.REFUSED)
366
+ relations = wf.access.act_relations(
367
+ tx, docket, route, seats, position=position
368
+ )
369
+ decision = self._wf_require(tx, context, wf.access.CLAIM, docket, relations)
370
+ wf.engine.claim(
371
+ tx,
372
+ docket["sys_id"],
373
+ position["sys_id"],
374
+ self._wf_actor(seats, decision, principal, tx, context),
375
+ release=payload.get("release") is True,
376
+ )
377
+ self._wf_after_change(tx, context, docket["sys_id"])
378
+ body = self._wf_docket_body(tx, context, docket["sys_id"], principal, seats)
379
+ context.response().set_body({"ok": True, "docket": body})
380
+
381
+ self._wf_guarded(tx, context, work)
382
+
383
+ def OnActionWorkflowFlag(self, tx, context):
384
+ wf = _workflow()
385
+ payload = context.payload() or {}
386
+ principal = self._wf_principal(context)
387
+ seats = self._wf_seats(tx, context, principal)
388
+
389
+ def work():
390
+ docket, route = self._wf_docket(tx, payload)
391
+ relations = wf.access.view_relations(tx, docket, route, seats)
392
+ self._wf_require(tx, context, wf.access.FLAG, docket, relations)
393
+ actor = wf.engine.Actor.person(seats)
394
+ if payload.get("clear") is not None:
395
+ wf.engine.clear_flag(tx, docket["sys_id"], payload.get("clear"), actor)
396
+ else:
397
+ note = payload.get("note")
398
+ if note is not None and not isinstance(note, str):
399
+ raise _Refusal(
400
+ BAD_REQUEST, [{"code": "note", "text": "note must be text."}]
401
+ )
402
+ wf.engine.flag(tx, docket["sys_id"], actor, note)
403
+ body = self._wf_docket_body(tx, context, docket["sys_id"], principal, seats)
404
+ context.response().set_body({"ok": True, "docket": body})
405
+
406
+ self._wf_guarded(tx, context, work)
407
+
408
+ # ── notices ────────────────────────────────────────────────────────────
409
+
410
+ def OnActionWorkflowNotices(self, tx, context):
411
+ wf = _workflow()
412
+ payload = context.payload() or {}
413
+ principal = self._wf_principal(context)
414
+ authz.require(self, tx, context, wf.access.NOTICES)
415
+ seats = self._wf_seats(tx, context, principal)
416
+
417
+ def work():
418
+ body = wf.notices.list_for(
419
+ tx,
420
+ seats.person,
421
+ unread_only=bool(payload.get("unread_only")),
422
+ cursor=payload.get("cursor"),
423
+ limit=payload.get("limit") or 50,
424
+ )
425
+ context.response().set_body(body)
426
+
427
+ self._wf_guarded(tx, context, work)
428
+
429
+ def OnActionWorkflowNoticeAck(self, tx, context):
430
+ wf = _workflow()
431
+ payload = context.payload() or {}
432
+ principal = self._wf_principal(context)
433
+ authz.require(self, tx, context, wf.access.NOTICE_ACK)
434
+ seats = self._wf_seats(tx, context, principal)
435
+
436
+ def work():
437
+ unread = wf.notices.ack(
438
+ tx,
439
+ seats.person,
440
+ notice_id=payload.get("notice"),
441
+ all=payload.get("all") is True,
442
+ )
443
+ context.response().set_body({"ok": True, "unread": unread})
444
+
445
+ self._wf_guarded(tx, context, work)
446
+
447
+
448
+ class WorkflowQueueMixin:
449
+ """Queue-handler actions: program jobs, the outbox drain, the timers.
450
+ Refused over HTTP (queue records and scheduled ticks only)."""
451
+
452
+ def OnActionWorkflowProgram(self, tx, context):
453
+ from velocity.aws.dirty_pipeline import refuse_http
454
+
455
+ refuse_http(self, "workflow-program")
456
+ wf = _workflow()
457
+ payload = context.payload() or {}
458
+ result = wf.engine.run_program(
459
+ tx, payload.get("position"), payload.get("attempt")
460
+ )
461
+ drained = wf.outbox.drain(tx, context)
462
+ context.response().set_body({"result": result, "drained": drained})
463
+
464
+ def OnActionWorkflowDrain(self, tx, context):
465
+ from velocity.aws.dirty_pipeline import refuse_http
466
+
467
+ refuse_http(self, "workflow-drain")
468
+ context.response().set_body({"drained": _workflow().outbox.drain(tx, context)})
469
+
470
+ def OnActionWorkflowSweep(self, tx, context):
471
+ from velocity.aws.dirty_pipeline import refuse_http
472
+
473
+ refuse_http(self, "workflow-sweep")
474
+ wf = _workflow()
475
+ swept = wf.engine.sweep(tx)
476
+ context.response().set_body(
477
+ {"swept": swept, "drained": wf.outbox.drain(tx, context)}
478
+ )
479
+
480
+
481
+ # every client action WorkflowMixin serves: ``client_actions = MINE | WORKFLOW_ACTIONS``
482
+ WORKFLOW_ACTIONS = action_names(WorkflowMixin)
483
+ # the queue handler's (never list these in client_actions)
484
+ WORKFLOW_QUEUE_ACTIONS = action_names(WorkflowQueueMixin)
485
+
486
+ __all__ = [
487
+ "WORKFLOW_ACTIONS",
488
+ "WORKFLOW_QUEUE_ACTIONS",
489
+ "WorkflowMixin",
490
+ "WorkflowQueueMixin",
491
+ ]
@@ -29,6 +29,9 @@ Groups:
29
29
  * ``tiles`` — tiles and who sees them (:mod:`velocity.aws.tiles`).
30
30
  * ``scheduler`` — ``sys_schedules`` (:mod:`velocity.aws.scheduler`).
31
31
  * ``rules`` — rules, rule tests and the decision log (:mod:`velocity.rules.storage`).
32
+ * ``workflow`` — routes, dockets, marks, notices (:mod:`velocity.workflow.schema`);
33
+ run :func:`velocity.workflow.ensure_schema` too for the append-only marks
34
+ and the possession constraint.
32
35
 
33
36
  Each module's ``SCHEMA`` maps a table name to ``Table.ensure_schema`` keyword
34
37
  arguments (``columns`` plus optional ``indexes`` / ``unique_indexes``). Two
@@ -67,6 +70,8 @@ SCHEMA_SOURCES: Dict[str, Tuple[str, ...]] = {
67
70
  "scheduler": ("velocity.aws.scheduler",),
68
71
  # data-driven rules (velocity.rules); opt-in, for apps that evaluate them
69
72
  "rules": ("velocity.rules.storage",),
73
+ # dockets on routes (velocity.workflow); opt-in, for apps that use them
74
+ "workflow": ("velocity.workflow.schema",),
70
75
  "payment": ("velocity.payment.schema",),
71
76
  }
72
77
  GROUPS = tuple(SCHEMA_SOURCES)
@@ -37,6 +37,7 @@ from .engine import ( # noqa: F401
37
37
  evaluate,
38
38
  run_rule_tests,
39
39
  set_status,
40
+ valid_rule_id,
40
41
  validate_rules,
41
42
  )
42
43
  from .expressions import ExpressionError # noqa: F401
@@ -107,5 +108,6 @@ __all__ = [
107
108
  "run_rule_tests",
108
109
  "save_rule",
109
110
  "set_status",
111
+ "valid_rule_id",
110
112
  "validate_rules",
111
113
  ]