tempest-fastapi-sdk 0.43.0__tar.gz → 0.44.0__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 (379) hide show
  1. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/CHANGELOG.md +30 -0
  2. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/PKG-INFO +2 -2
  3. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/README.md +1 -1
  4. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/index.en.md +1 -0
  5. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/index.md +1 -0
  6. tempest_fastapi_sdk-0.44.0/docs/recipes/outbox.en.md +121 -0
  7. tempest_fastapi_sdk-0.44.0/docs/recipes/outbox.md +120 -0
  8. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/reference.md +3 -0
  9. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/mkdocs.yml +2 -0
  10. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/pyproject.toml +1 -1
  11. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/__init__.py +7 -1
  12. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/db/__init__.py +6 -0
  13. tempest_fastapi_sdk-0.44.0/tempest_fastapi_sdk/db/outbox.py +352 -0
  14. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/db/repository.py +47 -0
  15. tempest_fastapi_sdk-0.44.0/tests/db/test_outbox.py +179 -0
  16. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/uv.lock +45 -45
  17. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/.github/workflows/ci.yml +0 -0
  18. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/.github/workflows/docs.yml +0 -0
  19. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/.github/workflows/release-pypi.yml +0 -0
  20. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/.gitignore +0 -0
  21. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/.python-version +0 -0
  22. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/CLAUDE.md +0 -0
  23. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/Makefile +0 -0
  24. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/architecture.en.md +0 -0
  25. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/architecture.md +0 -0
  26. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/changelog.en.md +0 -0
  27. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/changelog.md +0 -0
  28. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/contributing.en.md +0 -0
  29. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/contributing.md +0 -0
  30. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/index.en.md +0 -0
  31. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/index.md +0 -0
  32. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/installation.en.md +0 -0
  33. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/installation.md +0 -0
  34. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/learning/index.en.md +0 -0
  35. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/learning/index.md +0 -0
  36. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/learning/marketplace/api.en.md +0 -0
  37. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/learning/marketplace/api.md +0 -0
  38. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/learning/marketplace/business-rules.en.md +0 -0
  39. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/learning/marketplace/business-rules.md +0 -0
  40. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/learning/marketplace/domain.en.md +0 -0
  41. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/learning/marketplace/domain.md +0 -0
  42. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/learning/marketplace/flows.en.md +0 -0
  43. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/learning/marketplace/flows.md +0 -0
  44. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/learning/marketplace/index.en.md +0 -0
  45. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/learning/marketplace/index.md +0 -0
  46. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/migration.en.md +0 -0
  47. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/migration.md +0 -0
  48. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/admin.en.md +0 -0
  49. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/admin.md +0 -0
  50. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/auth-flow.en.md +0 -0
  51. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/auth-flow.md +0 -0
  52. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/br-helpers.en.md +0 -0
  53. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/br-helpers.md +0 -0
  54. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/cache.en.md +0 -0
  55. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/cache.md +0 -0
  56. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/cli.en.md +0 -0
  57. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/cli.md +0 -0
  58. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/database.en.md +0 -0
  59. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/database.md +0 -0
  60. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/downloads.en.md +0 -0
  61. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/downloads.md +0 -0
  62. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/email.en.md +0 -0
  63. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/email.md +0 -0
  64. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/http-client.en.md +0 -0
  65. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/http-client.md +0 -0
  66. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/http.en.md +0 -0
  67. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/http.md +0 -0
  68. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/idempotency.en.md +0 -0
  69. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/idempotency.md +0 -0
  70. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/logging.en.md +0 -0
  71. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/logging.md +0 -0
  72. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/metrics.en.md +0 -0
  73. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/metrics.md +0 -0
  74. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/mfa.en.md +0 -0
  75. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/mfa.md +0 -0
  76. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/observability.en.md +0 -0
  77. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/observability.md +0 -0
  78. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/queue-tasks.en.md +0 -0
  79. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/queue-tasks.md +0 -0
  80. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/realtime.en.md +0 -0
  81. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/realtime.md +0 -0
  82. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/security.en.md +0 -0
  83. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/security.md +0 -0
  84. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/sessions.en.md +0 -0
  85. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/sessions.md +0 -0
  86. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/storage.en.md +0 -0
  87. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/storage.md +0 -0
  88. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/testing.en.md +0 -0
  89. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/testing.md +0 -0
  90. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/uploads.en.md +0 -0
  91. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/uploads.md +0 -0
  92. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/utilities.en.md +0 -0
  93. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/utilities.md +0 -0
  94. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/webpush.en.md +0 -0
  95. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/webpush.md +0 -0
  96. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/websocket.en.md +0 -0
  97. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/recipes/websocket.md +0 -0
  98. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/reference.en.md +0 -0
  99. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/roadmap.en.md +0 -0
  100. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/roadmap.md +0 -0
  101. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/tutorial.en.md +0 -0
  102. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/docs/tutorial.md +0 -0
  103. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/scripts/extract_recipe.py +0 -0
  104. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/admin/__init__.py +0 -0
  105. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/admin/auth.py +0 -0
  106. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/admin/config.py +0 -0
  107. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/admin/forms.py +0 -0
  108. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/admin/router.py +0 -0
  109. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/admin/session.py +0 -0
  110. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/admin/site.py +0 -0
  111. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/admin/static/admin.css +0 -0
  112. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/admin/templates/base.html +0 -0
  113. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/admin/templates/dashboard.html +0 -0
  114. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/admin/templates/detail.html +0 -0
  115. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/admin/templates/form.html +0 -0
  116. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/admin/templates/list.html +0 -0
  117. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/admin/templates/login.html +0 -0
  118. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/admin/templates/logs.html +0 -0
  119. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/admin/templates/mfa.html +0 -0
  120. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/__init__.py +0 -0
  121. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/cookies.py +0 -0
  122. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/dependencies/__init__.py +0 -0
  123. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/dependencies/auth.py +0 -0
  124. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/handlers.py +0 -0
  125. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/middlewares/__init__.py +0 -0
  126. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/middlewares/body_size.py +0 -0
  127. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/middlewares/cors.py +0 -0
  128. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/middlewares/csrf.py +0 -0
  129. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/middlewares/idempotency.py +0 -0
  130. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/middlewares/rate_limit.py +0 -0
  131. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/middlewares/request_id.py +0 -0
  132. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/oauth.py +0 -0
  133. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/routers/__init__.py +0 -0
  134. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/routers/health.py +0 -0
  135. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/routers/logs.py +0 -0
  136. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/routers/metrics.py +0 -0
  137. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/routers/tool_spec.py +0 -0
  138. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/server.py +0 -0
  139. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/static.py +0 -0
  140. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/tracing.py +0 -0
  141. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/api/webhooks.py +0 -0
  142. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/auth/__init__.py +0 -0
  143. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/auth/page_renderer.py +0 -0
  144. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/auth/router.py +0 -0
  145. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/auth/schemas.py +0 -0
  146. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/auth/service.py +0 -0
  147. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/auth/templates/activation.html +0 -0
  148. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/auth/templates/activation_error.html +0 -0
  149. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/auth/templates/activation_success.html +0 -0
  150. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/auth/templates/password_reset.html +0 -0
  151. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/auth/templates/password_reset_error.html +0 -0
  152. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/auth/templates/password_reset_form.html +0 -0
  153. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/auth/templates/password_reset_success.html +0 -0
  154. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cache/__init__.py +0 -0
  155. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cache/decorator.py +0 -0
  156. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cache/redis_manager.py +0 -0
  157. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/__init__.py +0 -0
  158. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/README.md.tmpl +0 -0
  159. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/env.example.tmpl +0 -0
  160. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/gitignore.tmpl +0 -0
  161. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/main.py.tmpl +0 -0
  162. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/pyproject.toml.tmpl +0 -0
  163. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/src/__init__.py.tmpl +0 -0
  164. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/src/api/__init__.py.tmpl +0 -0
  165. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/src/api/app.py.tmpl +0 -0
  166. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/src/api/dependencies/__init__.py.tmpl +0 -0
  167. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/src/api/dependencies/auth.py.tmpl +0 -0
  168. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/src/api/dependencies/resources.py.tmpl +0 -0
  169. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/src/api/routers/__init__.py.tmpl +0 -0
  170. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/src/controllers/__init__.py.tmpl +0 -0
  171. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/src/core/__init__.py.tmpl +0 -0
  172. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/src/core/exceptions.py.tmpl +0 -0
  173. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/src/core/settings.py.tmpl +0 -0
  174. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/src/db/__init__.py.tmpl +0 -0
  175. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/src/db/models/__init__.py.tmpl +0 -0
  176. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/src/db/models/user.py.tmpl +0 -0
  177. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/src/db/repositories/__init__.py.tmpl +0 -0
  178. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/src/schemas/__init__.py.tmpl +0 -0
  179. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/src/server.py.tmpl +0 -0
  180. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/src/services/__init__.py.tmpl +0 -0
  181. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/src/utils/__init__.py.tmpl +0 -0
  182. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/tests/__init__.py.tmpl +0 -0
  183. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/_templates/tests/test_smoke.py.tmpl +0 -0
  184. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/db.py +0 -0
  185. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/docker_compose.py +0 -0
  186. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/generate.py +0 -0
  187. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/lint.py +0 -0
  188. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/main.py +0 -0
  189. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/new.py +0 -0
  190. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/src_layers.py +0 -0
  191. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/cli/user.py +0 -0
  192. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/controllers/__init__.py +0 -0
  193. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/controllers/base.py +0 -0
  194. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/core/__init__.py +0 -0
  195. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/core/context.py +0 -0
  196. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/core/enums.py +0 -0
  197. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/core/logging.py +0 -0
  198. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/db/_alembic_templates/__init__.py +0 -0
  199. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/db/_alembic_templates/env.py.template +0 -0
  200. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/db/alembic_hooks.py +0 -0
  201. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/db/connection.py +0 -0
  202. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/db/migrations.py +0 -0
  203. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/db/mixins.py +0 -0
  204. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/db/model.py +0 -0
  205. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/db/slow_query.py +0 -0
  206. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/db/user_model.py +0 -0
  207. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/db/user_recovery_code_model.py +0 -0
  208. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/db/user_token_model.py +0 -0
  209. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/exceptions/__init__.py +0 -0
  210. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/exceptions/base.py +0 -0
  211. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/exceptions/conflict.py +0 -0
  212. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/exceptions/forbidden.py +0 -0
  213. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/exceptions/jwt.py +0 -0
  214. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/exceptions/not_found.py +0 -0
  215. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/exceptions/too_many_requests.py +0 -0
  216. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/exceptions/unauthorized.py +0 -0
  217. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/exceptions/upload.py +0 -0
  218. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/exceptions/validation.py +0 -0
  219. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/py.typed +0 -0
  220. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/queue/__init__.py +0 -0
  221. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/queue/manager.py +0 -0
  222. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/schemas/__init__.py +0 -0
  223. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/schemas/base.py +0 -0
  224. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/schemas/link_headers.py +0 -0
  225. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/schemas/logs.py +0 -0
  226. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/schemas/pagination.py +0 -0
  227. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/schemas/response.py +0 -0
  228. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/services/__init__.py +0 -0
  229. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/services/base.py +0 -0
  230. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/sessions/__init__.py +0 -0
  231. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/sessions/dependencies.py +0 -0
  232. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/sessions/middleware.py +0 -0
  233. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/sessions/router.py +0 -0
  234. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/sessions/schemas.py +0 -0
  235. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/sessions/service.py +0 -0
  236. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/sessions/store.py +0 -0
  237. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/settings/__init__.py +0 -0
  238. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/settings/base.py +0 -0
  239. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/settings/mixins.py +0 -0
  240. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/sse/__init__.py +0 -0
  241. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/sse/event_stream.py +0 -0
  242. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/storage/__init__.py +0 -0
  243. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/storage/minio_client.py +0 -0
  244. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/tasks/__init__.py +0 -0
  245. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/tasks/manager.py +0 -0
  246. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/tasks/scheduler.py +0 -0
  247. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/testing/__init__.py +0 -0
  248. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/testing/database.py +0 -0
  249. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/utils/__init__.py +0 -0
  250. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/utils/client_ip.py +0 -0
  251. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/utils/datetime.py +0 -0
  252. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/utils/dict.py +0 -0
  253. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/utils/download.py +0 -0
  254. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/utils/email.py +0 -0
  255. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/utils/http_client.py +0 -0
  256. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/utils/jwt.py +0 -0
  257. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/utils/log.py +0 -0
  258. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/utils/metrics.py +0 -0
  259. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/utils/opaque_token.py +0 -0
  260. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/utils/password.py +0 -0
  261. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/utils/regex.py +0 -0
  262. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/utils/storage_backends.py +0 -0
  263. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/utils/throttle.py +0 -0
  264. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/utils/totp.py +0 -0
  265. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/utils/upload.py +0 -0
  266. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/webpush/__init__.py +0 -0
  267. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/webpush/dispatcher.py +0 -0
  268. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/webpush/schemas.py +0 -0
  269. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/websockets/__init__.py +0 -0
  270. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/websockets/hub.py +0 -0
  271. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/websockets/router.py +0 -0
  272. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tempest_fastapi_sdk/websockets/schemas.py +0 -0
  273. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/__init__.py +0 -0
  274. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/admin/__init__.py +0 -0
  275. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/admin/test_auth.py +0 -0
  276. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/admin/test_forms.py +0 -0
  277. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/admin/test_logs_nav.py +0 -0
  278. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/admin/test_mfa.py +0 -0
  279. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/admin/test_router.py +0 -0
  280. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/admin/test_site.py +0 -0
  281. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/admin/test_user_model.py +0 -0
  282. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/__init__.py +0 -0
  283. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/test_body_size.py +0 -0
  284. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/test_cookies.py +0 -0
  285. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/test_cors.py +0 -0
  286. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/test_csrf.py +0 -0
  287. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/test_dependencies_auth.py +0 -0
  288. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/test_handlers.py +0 -0
  289. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/test_health_router.py +0 -0
  290. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/test_idempotency.py +0 -0
  291. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/test_jwt_dependency.py +0 -0
  292. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/test_logs_router.py +0 -0
  293. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/test_oauth.py +0 -0
  294. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/test_prometheus.py +0 -0
  295. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/test_rate_limit.py +0 -0
  296. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/test_request_id_middleware.py +0 -0
  297. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/test_role_dependency.py +0 -0
  298. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/test_server.py +0 -0
  299. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/test_static.py +0 -0
  300. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/test_tool_spec.py +0 -0
  301. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/test_tracing.py +0 -0
  302. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/test_webhooks.py +0 -0
  303. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/api/test_webhooks_rsa.py +0 -0
  304. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/auth/__init__.py +0 -0
  305. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/auth/test_mfa.py +0 -0
  306. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/auth/test_service.py +0 -0
  307. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/cache/__init__.py +0 -0
  308. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/cache/test_decorator.py +0 -0
  309. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/cache/test_redis_manager.py +0 -0
  310. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/cli/__init__.py +0 -0
  311. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/cli/test_db.py +0 -0
  312. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/cli/test_docker_compose.py +0 -0
  313. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/cli/test_generate.py +0 -0
  314. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/cli/test_main.py +0 -0
  315. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/cli/test_user.py +0 -0
  316. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/conftest.py +0 -0
  317. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/controllers/__init__.py +0 -0
  318. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/controllers/test_base.py +0 -0
  319. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/core/__init__.py +0 -0
  320. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/core/test_context.py +0 -0
  321. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/core/test_enums.py +0 -0
  322. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/core/test_logging.py +0 -0
  323. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/db/__init__.py +0 -0
  324. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/db/test_alembic_hooks.py +0 -0
  325. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/db/test_bulk_ops.py +0 -0
  326. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/db/test_connection.py +0 -0
  327. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/db/test_migrations.py +0 -0
  328. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/db/test_mixins.py +0 -0
  329. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/db/test_model.py +0 -0
  330. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/db/test_repository.py +0 -0
  331. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/db/test_slow_query.py +0 -0
  332. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/exceptions/__init__.py +0 -0
  333. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/exceptions/test_exceptions.py +0 -0
  334. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/queue/__init__.py +0 -0
  335. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/queue/test_manager.py +0 -0
  336. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/schemas/__init__.py +0 -0
  337. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/schemas/test_base.py +0 -0
  338. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/schemas/test_cursor_pagination.py +0 -0
  339. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/schemas/test_link_headers.py +0 -0
  340. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/schemas/test_pagination.py +0 -0
  341. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/schemas/test_response.py +0 -0
  342. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/services/__init__.py +0 -0
  343. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/services/test_base.py +0 -0
  344. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/sessions/__init__.py +0 -0
  345. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/sessions/test_sessions.py +0 -0
  346. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/settings/__init__.py +0 -0
  347. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/settings/test_base.py +0 -0
  348. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/settings/test_mixins.py +0 -0
  349. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/sse/__init__.py +0 -0
  350. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/sse/test_event_stream.py +0 -0
  351. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/storage/__init__.py +0 -0
  352. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/storage/test_minio_client.py +0 -0
  353. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/tasks/__init__.py +0 -0
  354. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/tasks/test_manager.py +0 -0
  355. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/tasks/test_scheduler.py +0 -0
  356. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/testing/__init__.py +0 -0
  357. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/testing/test_database.py +0 -0
  358. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/utils/__init__.py +0 -0
  359. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/utils/test_client_ip.py +0 -0
  360. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/utils/test_datetime.py +0 -0
  361. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/utils/test_dict.py +0 -0
  362. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/utils/test_download.py +0 -0
  363. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/utils/test_email.py +0 -0
  364. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/utils/test_http_client.py +0 -0
  365. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/utils/test_jwt.py +0 -0
  366. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/utils/test_lazy_extras.py +0 -0
  367. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/utils/test_log.py +0 -0
  368. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/utils/test_metrics.py +0 -0
  369. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/utils/test_opaque_token.py +0 -0
  370. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/utils/test_password.py +0 -0
  371. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/utils/test_regex.py +0 -0
  372. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/utils/test_storage_backends.py +0 -0
  373. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/utils/test_throttle.py +0 -0
  374. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/utils/test_upload.py +0 -0
  375. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/webpush/__init__.py +0 -0
  376. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/webpush/test_dispatcher.py +0 -0
  377. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/webpush/test_schemas.py +0 -0
  378. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/websockets/__init__.py +0 -0
  379. {tempest_fastapi_sdk-0.43.0 → tempest_fastapi_sdk-0.44.0}/tests/websockets/test_hub_and_router.py +0 -0
@@ -5,6 +5,36 @@ All notable changes to **tempest-fastapi-sdk** are listed below.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.44.0] — 2026-06-11
9
+
10
+ ### Added
11
+
12
+ - **Transactional outbox.** New `BaseOutboxModel` (abstract — the
13
+ project subclasses it and picks `__tablename__`) carrying `topic`,
14
+ `payload` (JSON), `status`, `attempts` / `max_attempts`,
15
+ `available_at`, `sent_at` and `last_error`. `OutboxModel.new_event(
16
+ topic, payload)` builds a pending row.
17
+ - **`BaseRepository.save_with_outbox(model, event)`** inserts the
18
+ business row and the outbox event in the **same transaction**, so an
19
+ event can never reference a rolled-back row (and a committed row
20
+ always has its event durably queued) — the fix for the dual-write
21
+ problem.
22
+ - **`OutboxRelay`** drains pending rows and publishes them through a
23
+ caller-supplied async `publish` callable (no hard broker dependency
24
+ — works with `AsyncBrokerManager`, a webhook, a test spy). Marks each
25
+ row `sent`; on failure increments `attempts`, records `last_error`,
26
+ reschedules with exponential backoff, and marks `failed` once the
27
+ attempt budget is spent. Locks the batch with `FOR UPDATE SKIP
28
+ LOCKED` on PostgreSQL/MySQL (multi-worker safe), falling back to a
29
+ plain select on SQLite. `drain_once()` for one batch (tests/cron),
30
+ `run(poll_interval=...)` for the loop.
31
+ - **`OutboxStatus`** enum (`pending` / `sent` / `failed`).
32
+
33
+ ### Docs
34
+
35
+ - New bilingual recipe **Outbox transacional / Transactional outbox**,
36
+ added to the nav, the recipes index, and the API reference.
37
+
8
38
  ## [0.43.0] — 2026-06-11
9
39
 
10
40
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tempest-fastapi-sdk
3
- Version: 0.43.0
3
+ Version: 0.44.0
4
4
  Summary: Shared FastAPI building blocks: base schemas, ORM model, async repository, exceptions, pagination and settings — the conventions used across Tempest projects.
5
5
  Project-URL: Homepage, https://github.com/mauriciobenjamin700/tempest-fastapi-sdk
6
6
  Project-URL: Repository, https://github.com/mauriciobenjamin700/tempest-fastapi-sdk
@@ -238,7 +238,7 @@ Since `0.7.1` every optional dependency is imported lazily at first instantiatio
238
238
  | Module | Exports |
239
239
  | --- | --- |
240
240
  | `tempest_fastapi_sdk.schemas` | `BaseSchema`, `BaseResponseSchema`, `BasePaginationFilterSchema`, `BasePaginationSchema[T]`, `CursorPaginationFilterSchema`, `CursorPaginationSchema`, `LogEntrySchema`, `encode_cursor`, `decode_cursor`, `build_pagination_link_header` |
241
- | `tempest_fastapi_sdk.db` | `BaseModel`, `BaseUserModel`, `BaseUserTokenModel`, `BaseUserRecoveryCodeModel`, `make_user_recovery_code_model`, `UserTokenPurpose`, `BaseRepository[ModelType]`, `AsyncDatabaseManager`, `AlembicHelper`, `NAMING_CONVENTION`, `AuditMixin`, `SoftDeleteMixin`, `MFAMixin`, `BASE_COLUMN_ORDER`, `reorder_base_columns_first`, `compose_hooks`, `SlowQueryLogger` |
241
+ | `tempest_fastapi_sdk.db` | `BaseModel`, `BaseUserModel`, `BaseUserTokenModel`, `BaseUserRecoveryCodeModel`, `make_user_recovery_code_model`, `UserTokenPurpose`, `BaseRepository[ModelType]`, `AsyncDatabaseManager`, `AlembicHelper`, `NAMING_CONVENTION`, `AuditMixin`, `SoftDeleteMixin`, `MFAMixin`, `BASE_COLUMN_ORDER`, `reorder_base_columns_first`, `compose_hooks`, `SlowQueryLogger`, `BaseOutboxModel`, `OutboxRelay`, `OutboxStatus`, `BaseRepository.save_with_outbox` |
242
242
  | `tempest_fastapi_sdk.exceptions` | `AppException`, `NotFoundException`, `ConflictException`, `ValidationException`, `UnauthorizedException`, `ForbiddenException`, `InvalidTokenException`, `ExpiredTokenException`, `FileTooLargeException`, `InvalidFileTypeException`, `TooManyRequestsException` |
243
243
  | `tempest_fastapi_sdk.settings` | `BaseAppSettings`, `ServerSettings`, `LogSettings`, `DatabaseSettings`, `RedisSettings`, `RabbitMQSettings`, `JWTSettings`, `CORSSettings`, `EmailSettings`, `UploadSettings`, `TokenSettings`, `WebPushSettings`, `TaskIQSettings`, `MinIOSettings`, `AuthSettings` |
244
244
  | `tempest_fastapi_sdk.api` | `register_exception_handlers`, `app_exception_handler`, `apply_cors`, `make_health_router`, `make_logs_router`, `make_prometheus_router`, `make_prometheus_registry`, `PrometheusMiddleware`, `LogSource`, `make_tool_spec_router`, `make_token_dependency`, `make_bearer_token_dependency`, `make_jwt_user_dependency`, `make_role_dependency`, `make_permission_dependency`, `require_x_token`, `run_server`, `RequestIDMiddleware`, `RateLimitMiddleware`, `IdempotencyMiddleware`, `MemoryIdempotencyStore`, `RedisIdempotencyStore`, `BodySizeLimitMiddleware`, `CSRFMiddleware`, `make_csrf_token_dependency`, `WebhookSignatureVerifier`, `RSAWebhookSignatureVerifier`, OAuth2 (`GoogleOAuthClient`, `GitHubOAuthClient`, `OIDCProvider`), `HardenedStaticFiles`, `DEFAULT_STATIC_SECURITY_HEADERS`, `set_cookie`, `clear_cookie`, `SameSite`, `HealthCheck`, `setup_tracing` *(extra: `[otel]`)* |
@@ -136,7 +136,7 @@ Since `0.7.1` every optional dependency is imported lazily at first instantiatio
136
136
  | Module | Exports |
137
137
  | --- | --- |
138
138
  | `tempest_fastapi_sdk.schemas` | `BaseSchema`, `BaseResponseSchema`, `BasePaginationFilterSchema`, `BasePaginationSchema[T]`, `CursorPaginationFilterSchema`, `CursorPaginationSchema`, `LogEntrySchema`, `encode_cursor`, `decode_cursor`, `build_pagination_link_header` |
139
- | `tempest_fastapi_sdk.db` | `BaseModel`, `BaseUserModel`, `BaseUserTokenModel`, `BaseUserRecoveryCodeModel`, `make_user_recovery_code_model`, `UserTokenPurpose`, `BaseRepository[ModelType]`, `AsyncDatabaseManager`, `AlembicHelper`, `NAMING_CONVENTION`, `AuditMixin`, `SoftDeleteMixin`, `MFAMixin`, `BASE_COLUMN_ORDER`, `reorder_base_columns_first`, `compose_hooks`, `SlowQueryLogger` |
139
+ | `tempest_fastapi_sdk.db` | `BaseModel`, `BaseUserModel`, `BaseUserTokenModel`, `BaseUserRecoveryCodeModel`, `make_user_recovery_code_model`, `UserTokenPurpose`, `BaseRepository[ModelType]`, `AsyncDatabaseManager`, `AlembicHelper`, `NAMING_CONVENTION`, `AuditMixin`, `SoftDeleteMixin`, `MFAMixin`, `BASE_COLUMN_ORDER`, `reorder_base_columns_first`, `compose_hooks`, `SlowQueryLogger`, `BaseOutboxModel`, `OutboxRelay`, `OutboxStatus`, `BaseRepository.save_with_outbox` |
140
140
  | `tempest_fastapi_sdk.exceptions` | `AppException`, `NotFoundException`, `ConflictException`, `ValidationException`, `UnauthorizedException`, `ForbiddenException`, `InvalidTokenException`, `ExpiredTokenException`, `FileTooLargeException`, `InvalidFileTypeException`, `TooManyRequestsException` |
141
141
  | `tempest_fastapi_sdk.settings` | `BaseAppSettings`, `ServerSettings`, `LogSettings`, `DatabaseSettings`, `RedisSettings`, `RabbitMQSettings`, `JWTSettings`, `CORSSettings`, `EmailSettings`, `UploadSettings`, `TokenSettings`, `WebPushSettings`, `TaskIQSettings`, `MinIOSettings`, `AuthSettings` |
142
142
  | `tempest_fastapi_sdk.api` | `register_exception_handlers`, `app_exception_handler`, `apply_cors`, `make_health_router`, `make_logs_router`, `make_prometheus_router`, `make_prometheus_registry`, `PrometheusMiddleware`, `LogSource`, `make_tool_spec_router`, `make_token_dependency`, `make_bearer_token_dependency`, `make_jwt_user_dependency`, `make_role_dependency`, `make_permission_dependency`, `require_x_token`, `run_server`, `RequestIDMiddleware`, `RateLimitMiddleware`, `IdempotencyMiddleware`, `MemoryIdempotencyStore`, `RedisIdempotencyStore`, `BodySizeLimitMiddleware`, `CSRFMiddleware`, `make_csrf_token_dependency`, `WebhookSignatureVerifier`, `RSAWebhookSignatureVerifier`, OAuth2 (`GoogleOAuthClient`, `GitHubOAuthClient`, `OIDCProvider`), `HardenedStaticFiles`, `DEFAULT_STATIC_SECURITY_HEADERS`, `set_cookie`, `clear_cookie`, `SameSite`, `HealthCheck`, `setup_tracing` *(extra: `[otel]`)* |
@@ -15,6 +15,7 @@ Bite-sized "I want to wire X" walkthroughs. Each page starts with **what problem
15
15
  | **[Cache »](cache.md)** | `AsyncRedisManager`, `@cached` decorator |
16
16
  | **[Real-time »](realtime.md)** | Server-Sent Events (`EventStream`, `sse_response`) |
17
17
  | **[Queue & Tasks »](queue-tasks.md)** | FastStream (`AsyncBrokerManager`), TaskIQ (`AsyncTaskBrokerManager`), `AsyncTaskScheduler`, transactional outbox |
18
+ | **[Transactional outbox »](outbox.md)** | `BaseOutboxModel`, `OutboxRelay`, `save_with_outbox` — reliable events |
18
19
  | **[Transactional email »](email.md)** | `EmailUtils` — SMTP, text/HTML body, attachments, Jinja2 templates |
19
20
  | **[Web Push »](webpush.md)** | `WebPushDispatcher`, VAPID schemas, broadcast with pruning |
20
21
  | **[Logging »](logging.md)** | `LogUtils`, structured JSON logging, request-ID propagation |
@@ -15,6 +15,7 @@ Passo a passo curtos no estilo "quero conectar X". Cada página começa com **qu
15
15
  | **[Cache »](cache.md)** | `AsyncRedisManager`, decorator `@cached` |
16
16
  | **[Tempo real »](realtime.md)** | Server-Sent Events (`EventStream`, `sse_response`) |
17
17
  | **[Fila e Tarefas »](queue-tasks.md)** | FastStream (`AsyncBrokerManager`), TaskIQ (`AsyncTaskBrokerManager`), `AsyncTaskScheduler`, outbox transacional |
18
+ | **[Outbox transacional »](outbox.md)** | `BaseOutboxModel`, `OutboxRelay`, `save_with_outbox` — eventos confiáveis |
18
19
  | **[Email transacional »](email.md)** | `EmailUtils` — SMTP, corpo texto/HTML, anexos, templates Jinja2 |
19
20
  | **[Web Push »](webpush.md)** | `WebPushDispatcher`, schemas VAPID, broadcast com poda |
20
21
  | **[Logging »](logging.md)** | `LogUtils`, logging JSON estruturado, propagação de request-ID |
@@ -0,0 +1,121 @@
1
+ # Transactional outbox (reliable events)
2
+
3
+ When a handler **writes a row** and **publishes an event**, doing the two
4
+ as independent operations is fragile: if the process dies *after* the
5
+ commit but *before* the publish, the event is lost; if it dies after the
6
+ publish but before the commit, a phantom event references a row that never
7
+ existed. This is the **dual-write problem**.
8
+
9
+ The **outbox** pattern fixes it: write the business row **and** an
10
+ `outbox` row in the **same transaction**. Either both commit or neither
11
+ does. A separate *relay* reads the pending rows and publishes them to the
12
+ broker, marking each one sent. The broker can be down for minutes — the
13
+ events wait, durably, in the table.
14
+
15
+ !!! info "Where this fits"
16
+ It complements the [`AsyncBrokerManager`](queue-tasks.md): the broker
17
+ *publishes*, the outbox *guarantees* the event exists to be published.
18
+ The relay takes any publish callable — so it works with FastStream, a
19
+ webhook, whatever.
20
+
21
+ ## 1. The outbox table
22
+
23
+ `BaseOutboxModel` is abstract — the project creates the concrete table
24
+ (just like `BaseUserModel`):
25
+
26
+ ```python
27
+ from tempest_fastapi_sdk import BaseOutboxModel
28
+
29
+
30
+ class OutboxModel(BaseOutboxModel):
31
+ """The service's pending-events table."""
32
+
33
+ __tablename__ = "outbox"
34
+ ```
35
+
36
+ It ships `topic`, `payload` (JSON), `status`, `attempts`, `max_attempts`,
37
+ `available_at`, `sent_at` and `last_error` — on top of `BaseModel`'s four
38
+ canonical columns (`id` / `is_active` / `created_at` / `updated_at`).
39
+ Generate the migration with the [`AlembicHelper`](database.md) like any
40
+ other table.
41
+
42
+ ## 2. Write atomically
43
+
44
+ In the service/repository, use `save_with_outbox` instead of `add`: it
45
+ inserts the business model **and** the event in one transaction.
46
+
47
+ ```python
48
+ from tempest_fastapi_sdk import BaseRepository
49
+
50
+ from src.db.models import OrderModel, OutboxModel
51
+
52
+
53
+ async def place_order(repo: BaseRepository[OrderModel], data: dict[str, object]) -> OrderModel:
54
+ """Create the order and queue the event in the same transaction."""
55
+ order = OrderModel(**data)
56
+ event = OutboxModel.new_event("orders.created", {"order": data})
57
+ return await repo.save_with_outbox(order, event)
58
+ ```
59
+
60
+ If the `commit` fails (e.g. a unique constraint), **both** rows are rolled
61
+ back — no orphan event is ever left behind.
62
+
63
+ ## 3. Drain and publish (the relay)
64
+
65
+ `OutboxRelay` reads pending rows and calls your publish callable. It does
66
+ not import any specific broker — you pass the function:
67
+
68
+ ```python
69
+ import asyncio
70
+
71
+ from tempest_fastapi_sdk import AsyncDatabaseManager, BaseOutboxModel, OutboxRelay
72
+
73
+ from src.db.models import OutboxModel
74
+
75
+
76
+ async def run_relay(db: AsyncDatabaseManager, broker: object) -> None:
77
+ """Publish pending events continuously."""
78
+
79
+ async def publish(event: BaseOutboxModel) -> None:
80
+ """Forward one event to the broker."""
81
+ await broker.publish(event.payload, event.topic) # type: ignore[attr-defined]
82
+
83
+ relay: OutboxRelay = OutboxRelay(db, model=OutboxModel, publish=publish)
84
+ await relay.run(poll_interval=1.0) # loops until the task is cancelled
85
+ ```
86
+
87
+ Run the relay as a separate process/worker (or a task in the lifespan).
88
+ Each published event becomes `status="sent"` with `sent_at` set.
89
+
90
+ ### Failures and retry
91
+
92
+ If `publish` raises, the relay does **not** mark the event sent: it
93
+ increments `attempts`, records the error in `last_error`, and reschedules
94
+ the event with exponential backoff (`available_at` in the future). Once
95
+ `attempts` reaches `max_attempts`, the row becomes `status="failed"` and
96
+ stays in the table for manual inspection (never auto-retried again).
97
+
98
+ !!! tip "Multiple workers"
99
+ On PostgreSQL/MySQL the relay locks the batch with `FOR UPDATE SKIP
100
+ LOCKED`, so you can run **several** relay workers without publishing
101
+ the same event twice. On SQLite (no row locks) it falls back to a
102
+ plain `SELECT` — use a single worker.
103
+
104
+ ### Drain once (tests / cron)
105
+
106
+ For loop-free scenarios (a test, a cron job), call `drain_once()`, which
107
+ returns how many events were published:
108
+
109
+ ```python
110
+ published: int = await relay.drain_once()
111
+ ```
112
+
113
+ ## Recap
114
+
115
+ - `BaseOutboxModel` → concrete `OutboxModel(__tablename__="outbox")`.
116
+ - `repo.save_with_outbox(model, event)` writes business + event
117
+ **atomically**.
118
+ - `OutboxRelay(db, model=..., publish=...).run()` publishes pending rows,
119
+ with retry/backoff and `sent` / `failed` marking.
120
+ - `OutboxModel.new_event(topic, payload)` builds the event; `drain_once()`
121
+ drains one batch for tests/cron.
@@ -0,0 +1,120 @@
1
+ # Outbox transacional (eventos confiáveis)
2
+
3
+ Quando um handler **grava uma linha** e **publica um evento**, fazer as
4
+ duas coisas como operações independentes é frágil: se o processo morre
5
+ *depois* do commit mas *antes* do publish, o evento some; se morre depois
6
+ do publish mas antes do commit, sobra um evento fantasma apontando pra uma
7
+ linha que nunca existiu. Isso é o **dual-write problem**.
8
+
9
+ O padrão **outbox** resolve: grave a linha de negócio **e** uma linha
10
+ `outbox` na **mesma transação**. Ou as duas comitam, ou nenhuma. Um
11
+ *relay* separado lê as linhas pendentes e publica no broker, marcando cada
12
+ uma como enviada. O broker pode ficar minutos fora do ar — os eventos
13
+ esperam, duráveis, na tabela.
14
+
15
+ !!! info "Onde isso encaixa"
16
+ Complementa o [`AsyncBrokerManager`](queue-tasks.md): o broker
17
+ *publica*, o outbox *garante* que o evento existe pra ser publicado. O
18
+ relay usa qualquer callable de publish — então funciona com FastStream,
19
+ webhook, o que for.
20
+
21
+ ## 1. A tabela outbox
22
+
23
+ `BaseOutboxModel` é abstrata — o projeto cria a tabela concreta (igual a
24
+ `BaseUserModel`):
25
+
26
+ ```python
27
+ from tempest_fastapi_sdk import BaseOutboxModel
28
+
29
+
30
+ class OutboxModel(BaseOutboxModel):
31
+ """Tabela de eventos pendentes do serviço."""
32
+
33
+ __tablename__ = "outbox"
34
+ ```
35
+
36
+ Ela já traz `topic`, `payload` (JSON), `status`, `attempts`,
37
+ `max_attempts`, `available_at`, `sent_at` e `last_error` — além das quatro
38
+ colunas canônicas do `BaseModel` (`id` / `is_active` / `created_at` /
39
+ `updated_at`). Gere a migration com o [`AlembicHelper`](database.md) como
40
+ qualquer outra tabela.
41
+
42
+ ## 2. Gravar de forma atômica
43
+
44
+ No service/repository, use `save_with_outbox` em vez de `add`: ele insere o
45
+ modelo de negócio **e** o evento numa transação só.
46
+
47
+ ```python
48
+ from tempest_fastapi_sdk import BaseRepository
49
+
50
+ from src.db.models import OrderModel, OutboxModel
51
+
52
+
53
+ async def place_order(repo: BaseRepository[OrderModel], data: dict[str, object]) -> OrderModel:
54
+ """Cria o pedido e enfileira o evento na mesma transação."""
55
+ order = OrderModel(**data)
56
+ event = OutboxModel.new_event("orders.created", {"order": data})
57
+ return await repo.save_with_outbox(order, event)
58
+ ```
59
+
60
+ Se o `commit` falhar (ex.: constraint única), **as duas** linhas são
61
+ revertidas — nunca sobra evento órfão.
62
+
63
+ ## 3. Drenar e publicar (o relay)
64
+
65
+ `OutboxRelay` lê as linhas pendentes e chama o seu callable de publish. Ele
66
+ não importa nenhum broker específico — você passa a função:
67
+
68
+ ```python
69
+ import asyncio
70
+
71
+ from tempest_fastapi_sdk import AsyncDatabaseManager, BaseOutboxModel, OutboxRelay
72
+
73
+ from src.db.models import OutboxModel
74
+
75
+
76
+ async def run_relay(db: AsyncDatabaseManager, broker: object) -> None:
77
+ """Publica eventos pendentes continuamente."""
78
+
79
+ async def publish(event: BaseOutboxModel) -> None:
80
+ """Encaminha um evento pro broker."""
81
+ await broker.publish(event.payload, event.topic) # type: ignore[attr-defined]
82
+
83
+ relay: OutboxRelay = OutboxRelay(db, model=OutboxModel, publish=publish)
84
+ await relay.run(poll_interval=1.0) # loop até a task ser cancelada
85
+ ```
86
+
87
+ Rode o relay como um processo/worker separado (ou uma task no lifespan).
88
+ Cada evento publicado vira `status="sent"` com `sent_at` preenchido.
89
+
90
+ ### Falhas e retry
91
+
92
+ Se o `publish` levanta exceção, o relay **não** marca como enviado: ele
93
+ incrementa `attempts`, guarda o erro em `last_error` e reagenda o evento
94
+ com backoff exponencial (`available_at` no futuro). Quando `attempts`
95
+ chega em `max_attempts`, a linha vira `status="failed"` e fica na tabela
96
+ pra inspeção manual (nunca mais é retentada automaticamente).
97
+
98
+ !!! tip "Múltiplos workers"
99
+ Em PostgreSQL/MySQL o relay trava o lote com `FOR UPDATE SKIP LOCKED`,
100
+ então você pode rodar **vários** workers de relay sem publicar o mesmo
101
+ evento duas vezes. Em SQLite (sem lock de linha) ele cai pra um
102
+ `SELECT` simples — use um worker só.
103
+
104
+ ### Drenar uma vez (testes / cron)
105
+
106
+ Pra cenários sem loop (um teste, um cron job), chame `drain_once()`, que
107
+ devolve quantos eventos foram publicados:
108
+
109
+ ```python
110
+ published: int = await relay.drain_once()
111
+ ```
112
+
113
+ ## Recap
114
+
115
+ - `BaseOutboxModel` → tabela concreta `OutboxModel(__tablename__="outbox")`.
116
+ - `repo.save_with_outbox(model, event)` grava negócio + evento **atômico**.
117
+ - `OutboxRelay(db, model=..., publish=...).run()` publica os pendentes,
118
+ com retry/backoff e marcação `sent` / `failed`.
119
+ - `OutboxModel.new_event(topic, payload)` monta o evento; `drain_once()`
120
+ drena um lote pra testes/cron.
@@ -37,6 +37,9 @@ Gerada automaticamente a partir das docstrings do SDK via [`mkdocstrings`](https
37
37
  ::: tempest_fastapi_sdk.db.connection.AsyncDatabaseManager
38
38
  ::: tempest_fastapi_sdk.db.migrations.AlembicHelper
39
39
  ::: tempest_fastapi_sdk.db.slow_query.SlowQueryLogger
40
+ ::: tempest_fastapi_sdk.db.outbox.BaseOutboxModel
41
+ ::: tempest_fastapi_sdk.db.outbox.OutboxRelay
42
+ ::: tempest_fastapi_sdk.db.outbox.OutboxStatus
40
43
 
41
44
  ---
42
45
 
@@ -92,6 +92,7 @@ plugins:
92
92
  Tempo real: Real-time
93
93
  WebSocket router: WebSocket router
94
94
  Fila e Tarefas: Queue & Tasks
95
+ Outbox transacional: Transactional outbox
95
96
  Email transacional: Transactional email
96
97
  Web Push: Web Push
97
98
  Logging: Logging
@@ -205,6 +206,7 @@ nav:
205
206
  - Tempo real: recipes/realtime.md
206
207
  - WebSocket router: recipes/websocket.md
207
208
  - Fila e Tarefas: recipes/queue-tasks.md
209
+ - Outbox transacional: recipes/outbox.md
208
210
  - Email transacional: recipes/email.md
209
211
  - Web Push: recipes/webpush.md
210
212
  - Logging: recipes/logging.md
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tempest-fastapi-sdk"
3
- version = "0.43.0"
3
+ version = "0.44.0"
4
4
  description = "Shared FastAPI building blocks: base schemas, ORM model, async repository, exceptions, pagination and settings — the conventions used across Tempest projects."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -99,11 +99,14 @@ from tempest_fastapi_sdk.db import (
99
99
  AsyncDatabaseManager,
100
100
  AuditMixin,
101
101
  BaseModel,
102
+ BaseOutboxModel,
102
103
  BaseRepository,
103
104
  BaseUserModel,
104
105
  BaseUserRecoveryCodeModel,
105
106
  BaseUserTokenModel,
106
107
  MFAMixin,
108
+ OutboxRelay,
109
+ OutboxStatus,
107
110
  SlowQueryLogger,
108
111
  SoftDeleteMixin,
109
112
  UserTokenPurpose,
@@ -244,7 +247,7 @@ from tempest_fastapi_sdk.websockets import (
244
247
  make_websocket_router,
245
248
  )
246
249
 
247
- __version__: str = "0.43.0"
250
+ __version__: str = "0.44.0"
248
251
 
249
252
  __all__: list[str] = [
250
253
  "BASE_COLUMN_ORDER",
@@ -280,6 +283,7 @@ __all__: list[str] = [
280
283
  "BaseController",
281
284
  "BaseIntEnum",
282
285
  "BaseModel",
286
+ "BaseOutboxModel",
283
287
  "BasePaginationFilterSchema",
284
288
  "BasePaginationSchema",
285
289
  "BaseRepository",
@@ -348,6 +352,8 @@ __all__: list[str] = [
348
352
  "OIDCProvider",
349
353
  "ObjectStat",
350
354
  "OrderRef",
355
+ "OutboxRelay",
356
+ "OutboxStatus",
351
357
  "PasswordResetConfirmSchema",
352
358
  "PasswordResetRequestSchema",
353
359
  "PasswordResetResponseSchema",
@@ -21,6 +21,9 @@ from tempest_fastapi_sdk.db.mixins import MFAMixin as MFAMixin
21
21
  from tempest_fastapi_sdk.db.mixins import SoftDeleteMixin as SoftDeleteMixin
22
22
  from tempest_fastapi_sdk.db.model import NAMING_CONVENTION as NAMING_CONVENTION
23
23
  from tempest_fastapi_sdk.db.model import BaseModel as BaseModel
24
+ from tempest_fastapi_sdk.db.outbox import BaseOutboxModel as BaseOutboxModel
25
+ from tempest_fastapi_sdk.db.outbox import OutboxRelay as OutboxRelay
26
+ from tempest_fastapi_sdk.db.outbox import OutboxStatus as OutboxStatus
24
27
  from tempest_fastapi_sdk.db.repository import BaseRepository as BaseRepository
25
28
  from tempest_fastapi_sdk.db.slow_query import SlowQueryLogger as SlowQueryLogger
26
29
  from tempest_fastapi_sdk.db.user_model import BaseUserModel as BaseUserModel
@@ -45,11 +48,14 @@ __all__: list[str] = [
45
48
  "AsyncDatabaseManager",
46
49
  "AuditMixin",
47
50
  "BaseModel",
51
+ "BaseOutboxModel",
48
52
  "BaseRepository",
49
53
  "BaseUserModel",
50
54
  "BaseUserRecoveryCodeModel",
51
55
  "BaseUserTokenModel",
52
56
  "MFAMixin",
57
+ "OutboxRelay",
58
+ "OutboxStatus",
53
59
  "SlowQueryLogger",
54
60
  "SoftDeleteMixin",
55
61
  "UserTokenPurpose",