tempest-fastapi-sdk 0.61.0__tar.gz → 0.63.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.
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/CHANGELOG.md +58 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/PKG-INFO +10 -1
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/README.md +9 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/migration.en.md +30 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/migration.md +30 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/database.en.md +18 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/database.md +18 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/pyproject.toml +1 -1
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/__init__.py +1 -1
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/dependencies/auth.py +60 -16
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/service.py +31 -4
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/db/repository.py +23 -3
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/schemas/pagination.py +56 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/auth/test_service.py +63 -1
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/db/test_repository.py +32 -1
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/schemas/test_cursor_pagination.py +14 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/schemas/test_pagination.py +17 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/uv.lock +1 -1
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/.github/workflows/ci.yml +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/.github/workflows/docs.yml +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/.github/workflows/release-pypi.yml +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/.gitignore +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/.python-version +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/CLAUDE.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/Makefile +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/architecture.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/architecture.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/changelog.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/changelog.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/contributing.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/contributing.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/index.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/index.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/installation.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/installation.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/learning/index.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/learning/index.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/learning/marketplace/api.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/learning/marketplace/api.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/learning/marketplace/business-rules.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/learning/marketplace/business-rules.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/learning/marketplace/domain.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/learning/marketplace/domain.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/learning/marketplace/flows.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/learning/marketplace/flows.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/learning/marketplace/index.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/learning/marketplace/index.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/admin.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/admin.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/audit-trail.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/audit-trail.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/auth-flow.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/auth-flow.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/br-helpers.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/br-helpers.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/cache.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/cache.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/cli.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/cli.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/deploy-safety.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/deploy-safety.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/downloads.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/downloads.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/email.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/email.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/feature-flags.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/feature-flags.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/http-client.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/http-client.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/http.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/http.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/idempotency.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/idempotency.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/index.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/index.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/logging.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/logging.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/metrics.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/metrics.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/mfa.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/mfa.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/multi-tenant.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/multi-tenant.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/observability.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/observability.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/offline-sync.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/offline-sync.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/outbox.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/outbox.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/queue-tasks.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/queue-tasks.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/realtime.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/realtime.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/security.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/security.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/sessions.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/sessions.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/storage.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/storage.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/testing.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/testing.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/uploads.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/uploads.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/utilities.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/utilities.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/webpush.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/webpush.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/websocket.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/recipes/websocket.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/reference.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/reference.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/roadmap.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/roadmap.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/tutorial.en.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/docs/tutorial.md +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/mkdocs.yml +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/scripts/extract_recipe.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/admin/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/admin/auth.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/admin/config.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/admin/forms.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/admin/router.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/admin/session.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/admin/site.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/admin/static/admin.css +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/admin/templates/base.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/admin/templates/dashboard.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/admin/templates/detail.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/admin/templates/form.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/admin/templates/list.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/admin/templates/login.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/admin/templates/logs.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/admin/templates/mfa.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/cookies.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/dependencies/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/handlers.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/middlewares/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/middlewares/body_size.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/middlewares/cors.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/middlewares/csrf.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/middlewares/graceful.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/middlewares/idempotency.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/middlewares/rate_limit.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/middlewares/request_id.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/oauth.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/routers/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/routers/health.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/routers/logs.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/routers/metrics.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/routers/tool_spec.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/server.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/static.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/tracing.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/api/webhooks.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/guards.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/locale.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/page_renderer.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/router.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/schemas.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/templates/en-US/activation.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/templates/en-US/activation_error.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/templates/en-US/activation_success.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/templates/en-US/password_reset.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/templates/en-US/password_reset_error.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/templates/en-US/password_reset_form.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/templates/en-US/password_reset_success.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/templates/pt-BR/activation.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/templates/pt-BR/activation_error.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/templates/pt-BR/activation_success.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/templates/pt-BR/password_reset.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/templates/pt-BR/password_reset_error.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/templates/pt-BR/password_reset_form.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/templates/pt-BR/password_reset_success.html +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cache/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cache/decorator.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cache/invalidation.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cache/redis_manager.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/README.md.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/env.example.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/gitignore.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/main.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/pyproject.toml.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/src/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/src/api/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/src/api/app.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/src/api/dependencies/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/src/api/dependencies/auth.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/src/api/dependencies/resources.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/src/api/routers/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/src/controllers/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/src/core/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/src/core/exceptions.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/src/core/settings.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/src/db/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/src/db/models/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/src/db/models/user.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/src/db/repositories/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/src/schemas/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/src/server.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/src/services/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/src/utils/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/tests/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/_templates/tests/test_smoke.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/db.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/docker_compose.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/generate.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/lint.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/main.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/new.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/secrets.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/src_layers.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/cli/user.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/controllers/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/controllers/base.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/core/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/core/context.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/core/enums.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/core/logging.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/db/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/db/_alembic_templates/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/db/_alembic_templates/env.py.template +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/db/alembic_hooks.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/db/audit.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/db/connection.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/db/migrations.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/db/mixins.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/db/model.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/db/outbox.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/db/slow_query.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/db/tenant.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/db/user_model.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/db/user_recovery_code_model.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/db/user_token_model.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/exceptions/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/exceptions/base.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/exceptions/conflict.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/exceptions/forbidden.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/exceptions/i18n.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/exceptions/jwt.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/exceptions/not_found.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/exceptions/too_many_requests.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/exceptions/unauthorized.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/exceptions/upload.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/exceptions/validation.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/flags/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/flags/backends.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/flags/dependencies.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/flags/service.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/py.typed +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/queue/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/queue/manager.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/schemas/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/schemas/base.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/schemas/link_headers.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/schemas/logs.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/schemas/response.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/services/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/services/base.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/sessions/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/sessions/dependencies.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/sessions/middleware.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/sessions/router.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/sessions/schemas.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/sessions/service.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/sessions/store.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/settings/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/settings/base.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/settings/mixins.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/sse/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/sse/event_stream.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/storage/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/storage/minio_client.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/tasks/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/tasks/manager.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/tasks/scheduler.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/testing/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/testing/database.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/utils/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/utils/client_ip.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/utils/data/br_locations.json +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/utils/datetime.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/utils/dict.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/utils/download.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/utils/email.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/utils/http_client.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/utils/jwt.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/utils/locations.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/utils/log.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/utils/metrics.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/utils/opaque_token.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/utils/password.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/utils/regex.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/utils/storage_backends.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/utils/throttle.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/utils/totp.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/utils/upload.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/webpush/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/webpush/dispatcher.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/webpush/schemas.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/websockets/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/websockets/hub.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/websockets/router.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/websockets/schemas.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/admin/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/admin/test_auth.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/admin/test_forms.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/admin/test_logs_nav.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/admin/test_mfa.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/admin/test_router.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/admin/test_site.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/admin/test_user_model.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_body_size.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_cookies.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_cors.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_csrf.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_dependencies_auth.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_graceful.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_handlers.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_health_router.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_idempotency.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_jwt_dependency.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_logs_router.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_oauth.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_prometheus.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_rate_limit.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_rate_limit_extras.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_request_id_middleware.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_role_dependency.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_server.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_static.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_tool_spec.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_tracing.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_webhooks.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/api/test_webhooks_rsa.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/auth/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/auth/test_guards.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/auth/test_locale.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/auth/test_mfa.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/cache/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/cache/test_decorator.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/cache/test_invalidation.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/cache/test_redis_manager.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/cli/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/cli/test_db.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/cli/test_db_seed.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/cli/test_docker_compose.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/cli/test_generate.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/cli/test_main.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/cli/test_secrets.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/cli/test_user.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/conftest.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/controllers/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/controllers/test_base.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/core/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/core/test_context.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/core/test_enums.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/core/test_logging.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/db/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/db/test_alembic_hooks.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/db/test_audit.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/db/test_bulk_ops.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/db/test_connection.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/db/test_migrations.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/db/test_mixins.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/db/test_model.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/db/test_outbox.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/db/test_safe_upgrade.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/db/test_slow_query.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/db/test_tenant.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/exceptions/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/exceptions/test_exceptions.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/exceptions/test_i18n.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/flags/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/flags/test_flags.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/queue/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/queue/test_manager.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/schemas/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/schemas/test_base.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/schemas/test_link_headers.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/schemas/test_response.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/services/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/services/test_base.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/sessions/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/sessions/test_sessions.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/settings/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/settings/test_base.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/settings/test_mixins.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/sse/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/sse/test_event_stream.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/storage/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/storage/test_minio_client.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/tasks/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/tasks/test_manager.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/tasks/test_scheduler.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/testing/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/testing/test_database.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/utils/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/utils/test_client_ip.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/utils/test_datetime.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/utils/test_dict.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/utils/test_download.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/utils/test_email.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/utils/test_http_client.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/utils/test_jwt.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/utils/test_lazy_extras.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/utils/test_locations.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/utils/test_log.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/utils/test_metrics.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/utils/test_opaque_token.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/utils/test_password.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/utils/test_regex.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/utils/test_storage_backends.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/utils/test_throttle.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/utils/test_upload.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/webpush/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/webpush/test_dispatcher.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/webpush/test_schemas.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/websockets/__init__.py +0 -0
- {tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tests/websockets/test_hub_and_router.py +0 -0
|
@@ -5,6 +5,64 @@ 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.63.0] — 2026-06-21
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **`UserAuthService.current_user_dependency()` now loads the authenticated
|
|
13
|
+
user on the request-scoped session** (`db.session_dependency` by default)
|
|
14
|
+
instead of opening its own short-lived session through `load_user`.
|
|
15
|
+
Previously the returned `UserModel` was **detached** — mutating it and
|
|
16
|
+
committing/refreshing on the request's repository session raised
|
|
17
|
+
`InvalidRequestError: Instance is not persistent within this Session`,
|
|
18
|
+
and lazy-relationship access raised `DetachedInstanceError`. The user is
|
|
19
|
+
now attached to the same session repositories use. **Breaking** only for
|
|
20
|
+
apps whose repositories do not share the auth service's session callable;
|
|
21
|
+
pass `session_dependency=` to point both at the same provider. See the
|
|
22
|
+
**Migration guide** (`docs/migration.md`). The single-argument `user_loader` path
|
|
23
|
+
of `make_jwt_user_dependency` is unchanged; the new behavior is opt-in via
|
|
24
|
+
the new `session_dependency=` parameter (which `current_user_dependency`
|
|
25
|
+
now passes by default).
|
|
26
|
+
- **`BaseRepository.resolve()` re-attaches detached instances** via
|
|
27
|
+
`session.merge()` instead of returning them as-is. A detached model passed
|
|
28
|
+
to a mutating service is brought back into the active session, so the
|
|
29
|
+
subsequent `update()` commits instead of raising. `merge` issues a
|
|
30
|
+
`SELECT` only when the row is not already in the session's identity map.
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- **`make_jwt_user_dependency(..., session_dependency=...)`** — when given a
|
|
35
|
+
request-scoped session provider, the dependency injects it and calls the
|
|
36
|
+
two-argument loader `user_loader(subject, session)`, sharing the session
|
|
37
|
+
with the request's repositories.
|
|
38
|
+
- **`UserAuthService.current_user_dependency(session_dependency=...)`** —
|
|
39
|
+
override the session provider shared with repositories (defaults to
|
|
40
|
+
`self.db.session_dependency`). Now raises `RuntimeError` eagerly when the
|
|
41
|
+
service was built without `db=`.
|
|
42
|
+
|
|
43
|
+
## [0.62.0] — 2026-06-20
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
|
|
47
|
+
- **`BasePaginationFilterSchema.get_pagination_conditions()`** and
|
|
48
|
+
**`CursorPaginationFilterSchema.get_pagination_conditions()`** — the
|
|
49
|
+
counterpart to `get_conditions()`. Where `get_conditions()` strips the
|
|
50
|
+
pagination keys to expose the domain filters, this returns **only** the
|
|
51
|
+
pagination/sort keys (`page`/`page_size`/`order_by`/`ascending` for
|
|
52
|
+
offset, `cursor`/`limit`/`order_by`/`ascending` for cursor). A service
|
|
53
|
+
can now forward a filter schema to `paginate` / `cursor_paginate`
|
|
54
|
+
without manually unpacking the model:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
data = await repo.paginate(
|
|
58
|
+
filters=f.get_conditions(),
|
|
59
|
+
**f.get_pagination_conditions(),
|
|
60
|
+
)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
This replaces the `**filter_schema` idiom, which leaked domain filters
|
|
64
|
+
(e.g. `is_active`) into keyword arguments the repository does not accept.
|
|
65
|
+
|
|
8
66
|
## [0.61.0] — 2026-06-15
|
|
9
67
|
|
|
10
68
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tempest-fastapi-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.63.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
|
|
@@ -1167,6 +1167,15 @@ async def me(current: UserModel = Depends(get_current_user)) -> UserResponseSche
|
|
|
1167
1167
|
> get_current_user = auth_service.current_user_dependency()
|
|
1168
1168
|
> get_current_user_or_none = auth_service.current_user_dependency(soft=True)
|
|
1169
1169
|
> ```
|
|
1170
|
+
>
|
|
1171
|
+
> The authenticated user is loaded on the **request-scoped session**
|
|
1172
|
+
> (`db.session_dependency`), so it is attached to the same session your
|
|
1173
|
+
> repositories use — you can mutate it and `commit`/`refresh` without an
|
|
1174
|
+
> `InvalidRequestError: Instance is not persistent within this Session`.
|
|
1175
|
+
> Keep one shared session callable (`get_session = db.session_dependency`)
|
|
1176
|
+
> for FastAPI to deduplicate them; if your repositories depend on a
|
|
1177
|
+
> different `get_session` wrapper, pass it as
|
|
1178
|
+
> `current_user_dependency(session_dependency=get_session)`.
|
|
1170
1179
|
|
|
1171
1180
|
#### Soft auth (optional user)
|
|
1172
1181
|
|
|
@@ -1064,6 +1064,15 @@ async def me(current: UserModel = Depends(get_current_user)) -> UserResponseSche
|
|
|
1064
1064
|
> get_current_user = auth_service.current_user_dependency()
|
|
1065
1065
|
> get_current_user_or_none = auth_service.current_user_dependency(soft=True)
|
|
1066
1066
|
> ```
|
|
1067
|
+
>
|
|
1068
|
+
> The authenticated user is loaded on the **request-scoped session**
|
|
1069
|
+
> (`db.session_dependency`), so it is attached to the same session your
|
|
1070
|
+
> repositories use — you can mutate it and `commit`/`refresh` without an
|
|
1071
|
+
> `InvalidRequestError: Instance is not persistent within this Session`.
|
|
1072
|
+
> Keep one shared session callable (`get_session = db.session_dependency`)
|
|
1073
|
+
> for FastAPI to deduplicate them; if your repositories depend on a
|
|
1074
|
+
> different `get_session` wrapper, pass it as
|
|
1075
|
+
> `current_user_dependency(session_dependency=get_session)`.
|
|
1067
1076
|
|
|
1068
1077
|
#### Soft auth (optional user)
|
|
1069
1078
|
|
|
@@ -2,6 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
Breaking-change walkthroughs grouped by minor release. Stick to the version that matches what you're upgrading **from**.
|
|
4
4
|
|
|
5
|
+
## 0.63.0 — authenticated user loaded on the request session
|
|
6
|
+
|
|
7
|
+
Before 0.63.0, `UserAuthService.current_user_dependency()` loaded the authenticated user through `load_user`, which opened its **own** session (via `db.get_session_context()`) and closed it on exit. The `UserModel` handed to the route was therefore **detached**: mutating it and calling `commit`/`refresh` on the request session (the one your repositories use) raised
|
|
8
|
+
`InvalidRequestError: Instance is not persistent within this Session`.
|
|
9
|
+
|
|
10
|
+
From 0.63.0 the dependency loads the user on the **request session** (`db.session_dependency` by default) via `get_user(subject, session)`. The user is attached to the same session repositories use, so lazy-relationship reads and writes work without re-attaching anything.
|
|
11
|
+
|
|
12
|
+
!!! warning "Compatibility"
|
|
13
|
+
The auth dependency and your repositories must share the **same** session callable for FastAPI's sub-dependency cache to deduplicate them. The recommended pattern is already covered:
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
# resources.py
|
|
17
|
+
get_session = db.session_dependency # one object, reused
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
If you wrap the session in your own provider (`async def get_session(): ...`), pass it explicitly, otherwise the dependency opens a second session and the user is detached again:
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
get_current_user = auth.current_user_dependency(session_dependency=get_session)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
!!! info "Extra safety net"
|
|
27
|
+
`BaseRepository.resolve()` now re-attaches detached instances via `session.merge()`. Even if some flow still hands in a detached user, `resolve` brings it back into the active session instead of breaking — so services that worked around this (re-fetch by id before mutating) can drop the workaround.
|
|
28
|
+
|
|
29
|
+
### Verify
|
|
30
|
+
|
|
31
|
+
- Drop any "re-fetch by id before mutating the authenticated user" workaround — it's no longer needed.
|
|
32
|
+
- A single-argument `user_loader` passed to `make_jwt_user_dependency` keeps working. To share the request session, pass `session_dependency=` and use a two-argument loader `(subject, session)`.
|
|
33
|
+
|
|
34
|
+
## 0.8.0 — `ServerSettings` rename
|
|
5
35
|
|
|
6
36
|
0.8.0 renames every field on `ServerSettings`, extracts log fields to a new `LogSettings` mixin, and adds eleven other primitives. The renames are the only **breaking** changes — every new primitive is opt-in.
|
|
7
37
|
|
|
@@ -2,6 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
Passo a passo das mudanças que quebram compatibilidade, agrupadas por release minor. Siga a versão que casa com aquela **de onde** você está atualizando.
|
|
4
4
|
|
|
5
|
+
## 0.63.0 — usuário autenticado carregado na sessão de request
|
|
6
|
+
|
|
7
|
+
Antes da 0.63.0, `UserAuthService.current_user_dependency()` carregava o usuário autenticado chamando `load_user`, que abria a **própria** sessão (via `db.get_session_context()`) e a fechava ao terminar. O `UserModel` entregue à rota ficava **detached**: mutá-lo e dar `commit`/`refresh` na sessão de request (a dos seus repositories) levantava
|
|
8
|
+
`InvalidRequestError: Instance is not persistent within this Session`.
|
|
9
|
+
|
|
10
|
+
A partir da 0.63.0 a dependência carrega o usuário na **sessão de request** (`db.session_dependency` por padrão), via `get_user(subject, session)`. O usuário fica anexado à mesma sessão que os repositories usam, então leituras de relacionamentos lazy e escritas funcionam sem reanexar nada.
|
|
11
|
+
|
|
12
|
+
!!! warning "Compatibilidade"
|
|
13
|
+
A dependência de auth e seus repositories precisam compartilhar o **mesmo callable** de sessão para o cache de sub-dependências do FastAPI casar. Quem segue o padrão recomendado já está coberto:
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
# resources.py
|
|
17
|
+
get_session = db.session_dependency # um único objeto, reutilizado
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Se você embrulha a sessão num provider próprio (`async def get_session(): ...`), passe-o explicitamente para a dependência, senão ela abre uma segunda sessão e o usuário volta a ficar detached:
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
get_current_user = auth.current_user_dependency(session_dependency=get_session)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
!!! info "Defesa adicional"
|
|
27
|
+
`BaseRepository.resolve()` agora reanexa instâncias detached via `session.merge()`. Mesmo que algum fluxo ainda receba um usuário detached, o `resolve` o traz de volta à sessão ativa em vez de quebrar — então serviços que faziam workarounds manuais (re-fetch por id antes de mutar) podem removê-los.
|
|
28
|
+
|
|
29
|
+
### Verifique
|
|
30
|
+
|
|
31
|
+
- Remova qualquer workaround do tipo "re-fetch por id antes de mutar o usuário autenticado" — não é mais necessário.
|
|
32
|
+
- Se você passava um `user_loader` de um argumento para `make_jwt_user_dependency`, ele continua funcionando. Para compartilhar a sessão de request, passe `session_dependency=` e use um loader de dois argumentos `(subject, session)`.
|
|
33
|
+
|
|
34
|
+
## 0.8.0 — renomeação de `ServerSettings`
|
|
5
35
|
|
|
6
36
|
A 0.8.0 renomeia todos os campos de `ServerSettings`, extrai os campos de log para um novo mixin `LogSettings` e adiciona onze outros primitivos. As renomeações são as únicas mudanças **que quebram** — todo primitivo novo é opt-in.
|
|
7
37
|
|
|
@@ -650,6 +650,24 @@ class UserRepository(BaseRepository[UserModel]):
|
|
|
650
650
|
query, so custom joins still report a correct total. When `order_by` is
|
|
651
651
|
`None`, it orders by `created_at desc`.
|
|
652
652
|
|
|
653
|
+
!!! tip "Forward the schema without unpacking it by hand"
|
|
654
|
+
The `get_conditions()` / `get_pagination_conditions()` pair covers both
|
|
655
|
+
sides of the filter: the former returns only the domain filters, the
|
|
656
|
+
latter only the pagination keys (`page`, `page_size`, `order_by`,
|
|
657
|
+
`ascending`). The service forwards the filter straight through, no
|
|
658
|
+
`**f` — which would leak domain filters (`is_active`, etc.) as kwargs
|
|
659
|
+
the repository does not accept:
|
|
660
|
+
|
|
661
|
+
```python
|
|
662
|
+
data = await repo.paginate(
|
|
663
|
+
filters=f.get_conditions(),
|
|
664
|
+
**f.get_pagination_conditions(),
|
|
665
|
+
)
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
`CursorPaginationFilterSchema` exposes the same pair (with `cursor` /
|
|
669
|
+
`limit` instead of `page` / `page_size`).
|
|
670
|
+
|
|
653
671
|
### Cursor — when the table is large
|
|
654
672
|
|
|
655
673
|
Cursor pagination scales better than offset on large tables (no
|
|
@@ -651,6 +651,24 @@ class UserRepository(BaseRepository[UserModel]):
|
|
|
651
651
|
filtrada, então joins custom ainda reportam total correto. Quando
|
|
652
652
|
`order_by` é `None`, ordena por `created_at desc`.
|
|
653
653
|
|
|
654
|
+
!!! tip "Encaminhe o schema sem desempacotar à mão"
|
|
655
|
+
O par `get_conditions()` / `get_pagination_conditions()` cobre os dois
|
|
656
|
+
lados do filtro: o primeiro devolve só os filtros de domínio, o segundo
|
|
657
|
+
só as chaves de paginação (`page`, `page_size`, `order_by`,
|
|
658
|
+
`ascending`). Assim o service repassa o filtro direto, sem `**f` — que
|
|
659
|
+
vazaria filtros de domínio (`is_active`, etc.) como kwargs que o
|
|
660
|
+
repository não aceita:
|
|
661
|
+
|
|
662
|
+
```python
|
|
663
|
+
data = await repo.paginate(
|
|
664
|
+
filters=f.get_conditions(),
|
|
665
|
+
**f.get_pagination_conditions(),
|
|
666
|
+
)
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
`CursorPaginationFilterSchema` tem o mesmo par (com `cursor` / `limit`
|
|
670
|
+
no lugar de `page` / `page_size`).
|
|
671
|
+
|
|
654
672
|
### Cursor — quando a tabela é grande
|
|
655
673
|
|
|
656
674
|
A paginação por cursor escala melhor que offset em tabelas grandes (sem
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tempest-fastapi-sdk"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.63.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"
|
|
@@ -139,6 +139,7 @@ def make_jwt_user_dependency(
|
|
|
139
139
|
bearer_scheme: HTTPBearer | None = None,
|
|
140
140
|
subject_claim: str = "sub",
|
|
141
141
|
error_message: str = "Authorization token is missing or invalid",
|
|
142
|
+
session_dependency: Callable[..., Any] | None = None,
|
|
142
143
|
) -> Callable[..., Coroutine[Any, Any, Any]]:
|
|
143
144
|
"""Build a FastAPI dependency that returns the authenticated user.
|
|
144
145
|
|
|
@@ -150,15 +151,33 @@ def make_jwt_user_dependency(
|
|
|
150
151
|
4. Awaits ``user_loader(<id>)`` and returns whatever it yields.
|
|
151
152
|
|
|
152
153
|
``user_loader`` is the single seam where the service maps
|
|
153
|
-
``payload[subject_claim]`` to an actual user.
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
``payload[subject_claim]`` to an actual user.
|
|
155
|
+
|
|
156
|
+
**Session sharing.** When ``session_dependency`` is given, the
|
|
157
|
+
returned dependency declares it as a sub-dependency and calls the
|
|
158
|
+
two-argument ``user_loader(subject, session)`` with the
|
|
159
|
+
**request-scoped** session. Because FastAPI caches a sub-dependency
|
|
160
|
+
by its callable within a single request, the authenticated user is
|
|
161
|
+
loaded on the *same* session the request's repositories use — so the
|
|
162
|
+
instance stays attached and can be mutated / refreshed without the
|
|
163
|
+
``InvalidRequestError: Instance is not persistent within this
|
|
164
|
+
Session`` that a per-loader private session would cause. Pass the
|
|
165
|
+
**exact** callable your repositories depend on (e.g.
|
|
166
|
+
``db.session_dependency``); a different wrapper would resolve to a
|
|
167
|
+
second, distinct session and defeat the sharing.
|
|
168
|
+
|
|
169
|
+
When ``session_dependency`` is ``None`` the loader is called with a
|
|
170
|
+
single argument (``user_loader(subject)``) and owns its own session
|
|
171
|
+
lifecycle — useful outside a request scope, but it yields a
|
|
172
|
+
**detached** instance.
|
|
156
173
|
|
|
157
174
|
Args:
|
|
158
175
|
tokens (JWTUtils): The JWT helper used to verify the token.
|
|
159
176
|
user_loader (Callable[..., Coroutine[Any, Any, Any]]): Async
|
|
160
177
|
callable that receives the subject (typically the user id
|
|
161
|
-
as a string) and returns the loaded user.
|
|
178
|
+
as a string) and returns the loaded user. When
|
|
179
|
+
``session_dependency`` is set it is called as
|
|
180
|
+
``user_loader(subject, session)``. Raise
|
|
162
181
|
:class:`UnauthorizedException` or
|
|
163
182
|
:class:`NotFoundException` from inside the loader when the
|
|
164
183
|
user no longer exists.
|
|
@@ -169,6 +188,10 @@ def make_jwt_user_dependency(
|
|
|
169
188
|
Defaults to ``"sub"``.
|
|
170
189
|
error_message (str): Message attached to the raised
|
|
171
190
|
:class:`UnauthorizedException` when ``soft`` is ``False``.
|
|
191
|
+
session_dependency (Callable[..., Any] | None): The
|
|
192
|
+
request-scoped session provider to share with repositories.
|
|
193
|
+
When set, ``user_loader`` is called with ``(subject,
|
|
194
|
+
session)``.
|
|
172
195
|
|
|
173
196
|
Returns:
|
|
174
197
|
Callable[..., Coroutine[Any, Any, Any]]: An async FastAPI
|
|
@@ -181,20 +204,41 @@ def make_jwt_user_dependency(
|
|
|
181
204
|
error_message=error_message,
|
|
182
205
|
)
|
|
183
206
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
if subject is None:
|
|
191
|
-
if soft:
|
|
207
|
+
if session_dependency is None:
|
|
208
|
+
|
|
209
|
+
async def _current_user_owns_session(
|
|
210
|
+
payload: dict[str, Any] | None = Depends(decode_bearer),
|
|
211
|
+
) -> Any:
|
|
212
|
+
if payload is None:
|
|
192
213
|
return None
|
|
193
|
-
|
|
194
|
-
|
|
214
|
+
subject = payload.get(subject_claim)
|
|
215
|
+
if subject is None:
|
|
216
|
+
if soft:
|
|
217
|
+
return None
|
|
218
|
+
raise UnauthorizedException(message=error_message)
|
|
219
|
+
return await user_loader(subject)
|
|
220
|
+
|
|
221
|
+
dependency = _current_user_owns_session
|
|
222
|
+
|
|
223
|
+
else:
|
|
224
|
+
|
|
225
|
+
async def _current_user_shared_session(
|
|
226
|
+
payload: dict[str, Any] | None = Depends(decode_bearer),
|
|
227
|
+
session: Any = Depends(session_dependency),
|
|
228
|
+
) -> Any:
|
|
229
|
+
if payload is None:
|
|
230
|
+
return None
|
|
231
|
+
subject = payload.get(subject_claim)
|
|
232
|
+
if subject is None:
|
|
233
|
+
if soft:
|
|
234
|
+
return None
|
|
235
|
+
raise UnauthorizedException(message=error_message)
|
|
236
|
+
return await user_loader(subject, session)
|
|
237
|
+
|
|
238
|
+
dependency = _current_user_shared_session
|
|
195
239
|
|
|
196
|
-
|
|
197
|
-
return
|
|
240
|
+
dependency.__doc__ = "Decode the bearer JWT and resolve the authenticated user."
|
|
241
|
+
return dependency
|
|
198
242
|
|
|
199
243
|
|
|
200
244
|
def make_role_dependency(
|
{tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/auth/service.py
RENAMED
|
@@ -528,11 +528,12 @@ class UserAuthService:
|
|
|
528
528
|
self,
|
|
529
529
|
*,
|
|
530
530
|
soft: bool = False,
|
|
531
|
+
session_dependency: Callable[..., Any] | None = None,
|
|
531
532
|
) -> Callable[..., Coroutine[Any, Any, Any]]:
|
|
532
533
|
"""Build a FastAPI dependency that returns the authenticated user.
|
|
533
534
|
|
|
534
535
|
Wraps :func:`tempest_fastapi_sdk.make_jwt_user_dependency` with
|
|
535
|
-
this service's own :class:`JWTUtils` and :meth:`
|
|
536
|
+
this service's own :class:`JWTUtils` and :meth:`get_user`, so
|
|
536
537
|
the bearer token is verified with the **same** secret the
|
|
537
538
|
service signs with — there is no second ``JWTUtils`` to keep in
|
|
538
539
|
sync. Mount it on any of your routes:
|
|
@@ -542,23 +543,49 @@ class UserAuthService:
|
|
|
542
543
|
... soft=True
|
|
543
544
|
... )
|
|
544
545
|
|
|
545
|
-
|
|
546
|
-
|
|
546
|
+
The authenticated user is loaded on the **request-scoped**
|
|
547
|
+
session (``self.db.session_dependency`` by default), so it is
|
|
548
|
+
attached to the same session the request's repositories use and
|
|
549
|
+
can be mutated / refreshed without an
|
|
550
|
+
``InvalidRequestError: Instance is not persistent within this
|
|
551
|
+
Session``. If your repositories depend on a different session
|
|
552
|
+
callable (e.g. a project-local ``get_session`` wrapper), pass it
|
|
553
|
+
as ``session_dependency`` so both resolve to the *same* request
|
|
554
|
+
session — FastAPI caches a sub-dependency by its callable, so a
|
|
555
|
+
distinct wrapper would open a second session and detach the user.
|
|
556
|
+
|
|
557
|
+
Requires the service to have been built with ``db=``.
|
|
547
558
|
|
|
548
559
|
Args:
|
|
549
560
|
soft (bool): When ``True``, the dependency returns ``None``
|
|
550
561
|
instead of raising on a missing / invalid token — for
|
|
551
562
|
endpoints that work both authenticated and anonymous.
|
|
563
|
+
session_dependency (Callable[..., Any] | None): The
|
|
564
|
+
request-scoped session provider to share with
|
|
565
|
+
repositories. Defaults to ``self.db.session_dependency``.
|
|
552
566
|
|
|
553
567
|
Returns:
|
|
554
568
|
Callable[..., Coroutine[Any, Any, Any]]: An async FastAPI
|
|
555
569
|
dependency yielding the user (or ``None`` in soft mode).
|
|
570
|
+
|
|
571
|
+
Raises:
|
|
572
|
+
RuntimeError: When the service was created without ``db=``.
|
|
556
573
|
"""
|
|
574
|
+
if self.db is None:
|
|
575
|
+
raise RuntimeError(
|
|
576
|
+
"UserAuthService was created without `db=`; pass an "
|
|
577
|
+
"AsyncDatabaseManager to use current_user_dependency."
|
|
578
|
+
)
|
|
557
579
|
from tempest_fastapi_sdk.api.dependencies.auth import (
|
|
558
580
|
make_jwt_user_dependency,
|
|
559
581
|
)
|
|
560
582
|
|
|
561
|
-
return make_jwt_user_dependency(
|
|
583
|
+
return make_jwt_user_dependency(
|
|
584
|
+
self.jwt,
|
|
585
|
+
self.get_user,
|
|
586
|
+
soft=soft,
|
|
587
|
+
session_dependency=session_dependency or self.db.session_dependency,
|
|
588
|
+
)
|
|
562
589
|
|
|
563
590
|
# ------------------------------------------------------------------
|
|
564
591
|
# Authorization guards (imperative, on an already-loaded user)
|
{tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/db/repository.py
RENAMED
|
@@ -9,7 +9,16 @@ from datetime import date, datetime
|
|
|
9
9
|
from typing import Any, Generic, List, TypeVar, cast
|
|
10
10
|
from uuid import UUID
|
|
11
11
|
|
|
12
|
-
from sqlalchemy import
|
|
12
|
+
from sqlalchemy import (
|
|
13
|
+
CursorResult,
|
|
14
|
+
Select,
|
|
15
|
+
delete,
|
|
16
|
+
func,
|
|
17
|
+
insert,
|
|
18
|
+
inspect,
|
|
19
|
+
select,
|
|
20
|
+
update,
|
|
21
|
+
)
|
|
13
22
|
from sqlalchemy.exc import IntegrityError
|
|
14
23
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
15
24
|
|
|
@@ -337,14 +346,23 @@ class BaseRepository(Generic[ModelType]):
|
|
|
337
346
|
``if isinstance(x, UUID): ... else: ...`` boilerplate services
|
|
338
347
|
reimplement whenever a method takes ``UUID | Model``.
|
|
339
348
|
|
|
349
|
+
When given a **detached** instance (one whose session has been
|
|
350
|
+
closed — e.g. a user loaded by an auth dependency on its own
|
|
351
|
+
short-lived session) it is re-attached to this repository's
|
|
352
|
+
session via :meth:`AsyncSession.merge`. Returning it as-is would
|
|
353
|
+
later raise ``InvalidRequestError: Instance is not persistent
|
|
354
|
+
within this Session`` on the first ``commit`` / ``refresh``.
|
|
355
|
+
``merge`` issues a ``SELECT`` only when the row is not already in
|
|
356
|
+
this session's identity map.
|
|
357
|
+
|
|
340
358
|
Args:
|
|
341
359
|
ref (UUID | ModelType): The primary key to look up, or an
|
|
342
|
-
already-loaded instance to return
|
|
360
|
+
already-loaded instance to attach and return.
|
|
343
361
|
for_update (bool): Whether to acquire a row-level lock when
|
|
344
362
|
``ref`` is a ``UUID`` (ignored when an instance is given).
|
|
345
363
|
|
|
346
364
|
Returns:
|
|
347
|
-
ModelType: The resolved instance.
|
|
365
|
+
ModelType: The resolved instance, attached to this session.
|
|
348
366
|
|
|
349
367
|
Raises:
|
|
350
368
|
AppException: ``self.not_found_exception`` when ``ref`` is a
|
|
@@ -352,6 +370,8 @@ class BaseRepository(Generic[ModelType]):
|
|
|
352
370
|
"""
|
|
353
371
|
if isinstance(ref, UUID):
|
|
354
372
|
return await self.get_by_id(ref, for_update=for_update)
|
|
373
|
+
if inspect(ref).detached:
|
|
374
|
+
return await self.session.merge(ref)
|
|
355
375
|
return ref
|
|
356
376
|
|
|
357
377
|
async def exists(self, filters: dict[str, Any]) -> bool:
|
{tempest_fastapi_sdk-0.61.0 → tempest_fastapi_sdk-0.63.0}/tempest_fastapi_sdk/schemas/pagination.py
RENAMED
|
@@ -94,6 +94,34 @@ class BasePaginationFilterSchema(BaseSchema):
|
|
|
94
94
|
exclude=["page", "page_size", "order_by", "ascending"],
|
|
95
95
|
)
|
|
96
96
|
|
|
97
|
+
def get_pagination_conditions(self) -> dict[str, Any]:
|
|
98
|
+
"""Return only the pagination and sort keyword arguments.
|
|
99
|
+
|
|
100
|
+
Complements :meth:`get_conditions`: where that method strips the
|
|
101
|
+
pagination keys to expose the domain filters, this one keeps only
|
|
102
|
+
the pagination keys (``page``, ``page_size``, ``order_by``,
|
|
103
|
+
``ascending``). Together they let a service forward a filter
|
|
104
|
+
schema to :meth:`BaseRepository.paginate` without manually
|
|
105
|
+
unpacking the model, which would also leak domain filters such as
|
|
106
|
+
``is_active`` into kwargs the repository does not accept:
|
|
107
|
+
|
|
108
|
+
.. code-block:: python
|
|
109
|
+
|
|
110
|
+
data = await repo.paginate(
|
|
111
|
+
filters=f.get_conditions(),
|
|
112
|
+
**f.get_pagination_conditions(),
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
dict[str, Any]: The pagination/sort keyword arguments.
|
|
117
|
+
"""
|
|
118
|
+
return {
|
|
119
|
+
"page": self.page,
|
|
120
|
+
"page_size": self.page_size,
|
|
121
|
+
"order_by": self.order_by,
|
|
122
|
+
"ascending": self.ascending,
|
|
123
|
+
}
|
|
124
|
+
|
|
97
125
|
|
|
98
126
|
T = TypeVar("T", bound=BaseSchema)
|
|
99
127
|
|
|
@@ -210,6 +238,34 @@ class CursorPaginationFilterSchema(BaseSchema):
|
|
|
210
238
|
exclude=["cursor", "limit", "order_by", "ascending"],
|
|
211
239
|
)
|
|
212
240
|
|
|
241
|
+
def get_pagination_conditions(self) -> dict[str, Any]:
|
|
242
|
+
"""Return only the cursor pagination and sort keyword arguments.
|
|
243
|
+
|
|
244
|
+
Complements :meth:`get_conditions`: where that method strips the
|
|
245
|
+
pagination keys to expose the domain filters, this one keeps only
|
|
246
|
+
the pagination keys (``cursor``, ``limit``, ``order_by``,
|
|
247
|
+
``ascending``). Together they let a service forward a filter
|
|
248
|
+
schema to :meth:`BaseRepository.cursor_paginate` without manually
|
|
249
|
+
unpacking the model, which would also leak domain filters into
|
|
250
|
+
kwargs the repository does not accept:
|
|
251
|
+
|
|
252
|
+
.. code-block:: python
|
|
253
|
+
|
|
254
|
+
data = await repo.cursor_paginate(
|
|
255
|
+
filters=f.get_conditions(),
|
|
256
|
+
**f.get_pagination_conditions(),
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
dict[str, Any]: The cursor pagination/sort keyword arguments.
|
|
261
|
+
"""
|
|
262
|
+
return {
|
|
263
|
+
"cursor": self.cursor,
|
|
264
|
+
"limit": self.limit,
|
|
265
|
+
"order_by": self.order_by,
|
|
266
|
+
"ascending": self.ascending,
|
|
267
|
+
}
|
|
268
|
+
|
|
213
269
|
|
|
214
270
|
class CursorPaginationSchema(BaseSchema, Generic[T]):
|
|
215
271
|
"""Generic envelope returned by cursor-paginated endpoints.
|
|
@@ -9,7 +9,7 @@ from typing import Any
|
|
|
9
9
|
import pytest
|
|
10
10
|
from fastapi import Depends, FastAPI
|
|
11
11
|
from httpx import ASGITransport, AsyncClient
|
|
12
|
-
from sqlalchemy import select
|
|
12
|
+
from sqlalchemy import inspect, select
|
|
13
13
|
from sqlalchemy.ext.asyncio import (
|
|
14
14
|
AsyncSession,
|
|
15
15
|
async_sessionmaker,
|
|
@@ -1507,6 +1507,68 @@ class TestCurrentUserResolution:
|
|
|
1507
1507
|
finally:
|
|
1508
1508
|
await db.disconnect()
|
|
1509
1509
|
|
|
1510
|
+
async def test_current_user_attached_to_request_session(self) -> None:
|
|
1511
|
+
"""The authenticated user is attached to the request-scoped session.
|
|
1512
|
+
|
|
1513
|
+
Regression for ``InvalidRequestError: Instance is not persistent
|
|
1514
|
+
within this Session``: loading the user on a private auth session
|
|
1515
|
+
yielded a *detached* instance, so mutating it on the request's
|
|
1516
|
+
repository session never persisted (and ``refresh`` raised). The
|
|
1517
|
+
dependency now shares ``db.session_dependency`` with the route, so
|
|
1518
|
+
the user is live and a mutation commits.
|
|
1519
|
+
"""
|
|
1520
|
+
db, service = await _db_service()
|
|
1521
|
+
try:
|
|
1522
|
+
async with db.get_session_context() as s:
|
|
1523
|
+
user, _ = await service.signup(
|
|
1524
|
+
s,
|
|
1525
|
+
email="attached@a.com",
|
|
1526
|
+
password="strong-pass-12-chars",
|
|
1527
|
+
)
|
|
1528
|
+
user.is_active = True
|
|
1529
|
+
await s.commit()
|
|
1530
|
+
await s.refresh(user)
|
|
1531
|
+
user_id = user.id
|
|
1532
|
+
access, _ = service.issue_jwt_pair(user)
|
|
1533
|
+
|
|
1534
|
+
get_current_user = service.current_user_dependency()
|
|
1535
|
+
app = FastAPI()
|
|
1536
|
+
|
|
1537
|
+
@app.post("/deactivate")
|
|
1538
|
+
async def deactivate(
|
|
1539
|
+
current: Any = Depends(get_current_user),
|
|
1540
|
+
session: AsyncSession = Depends(db.session_dependency),
|
|
1541
|
+
) -> dict[str, bool]:
|
|
1542
|
+
# The user is attached to this very request session: its
|
|
1543
|
+
# underlying sync Session is the dependency's shared one.
|
|
1544
|
+
same_session = inspect(current).session is session.sync_session
|
|
1545
|
+
current.is_active = False
|
|
1546
|
+
await session.commit()
|
|
1547
|
+
await session.refresh(current) # would raise if detached
|
|
1548
|
+
return {"same_session": same_session}
|
|
1549
|
+
|
|
1550
|
+
async with AsyncClient(
|
|
1551
|
+
transport=ASGITransport(app=app), base_url="http://t"
|
|
1552
|
+
) as client:
|
|
1553
|
+
resp = await client.post(
|
|
1554
|
+
"/deactivate", headers={"Authorization": f"Bearer {access}"}
|
|
1555
|
+
)
|
|
1556
|
+
assert resp.status_code == 200
|
|
1557
|
+
assert resp.json() == {"same_session": True}
|
|
1558
|
+
|
|
1559
|
+
# The mutation actually hit the database.
|
|
1560
|
+
async with db.get_session_context() as verify:
|
|
1561
|
+
reloaded = await service.get_user(user_id, verify)
|
|
1562
|
+
assert reloaded.is_active is False
|
|
1563
|
+
finally:
|
|
1564
|
+
await db.disconnect()
|
|
1565
|
+
|
|
1566
|
+
async def test_current_user_without_db_raises(self) -> None:
|
|
1567
|
+
"""``current_user_dependency`` needs ``db=`` to share its session."""
|
|
1568
|
+
service = _service() # built without db=
|
|
1569
|
+
with pytest.raises(RuntimeError):
|
|
1570
|
+
service.current_user_dependency()
|
|
1571
|
+
|
|
1510
1572
|
async def test_current_user_dependency_soft_returns_none(self) -> None:
|
|
1511
1573
|
db, service = await _db_service()
|
|
1512
1574
|
try:
|