tempest-fastapi-sdk 0.46.0__tar.gz → 0.48.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.46.0 → tempest_fastapi_sdk-0.48.0}/CHANGELOG.md +39 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/PKG-INFO +18 -3
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/README.md +17 -2
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/cli.en.md +58 -1
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/cli.md +58 -1
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/index.en.md +1 -1
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/index.md +1 -1
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/pyproject.toml +1 -1
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/__init__.py +1 -1
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/env.example.tmpl +5 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/src/api/app.py.tmpl +5 -4
- tempest_fastapi_sdk-0.48.0/tempest_fastapi_sdk/cli/_templates/src/core/settings.py.tmpl +61 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/db.py +113 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/main.py +2 -0
- tempest_fastapi_sdk-0.48.0/tempest_fastapi_sdk/cli/secrets.py +159 -0
- tempest_fastapi_sdk-0.48.0/tests/cli/test_db_seed.py +107 -0
- tempest_fastapi_sdk-0.48.0/tests/cli/test_secrets.py +78 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/uv.lock +1 -1
- tempest_fastapi_sdk-0.46.0/tempest_fastapi_sdk/cli/_templates/src/core/settings.py.tmpl +0 -39
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/.github/workflows/ci.yml +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/.github/workflows/docs.yml +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/.github/workflows/release-pypi.yml +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/.gitignore +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/.python-version +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/CLAUDE.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/Makefile +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/architecture.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/architecture.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/changelog.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/changelog.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/contributing.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/contributing.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/index.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/index.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/installation.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/installation.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/learning/index.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/learning/index.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/learning/marketplace/api.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/learning/marketplace/api.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/learning/marketplace/business-rules.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/learning/marketplace/business-rules.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/learning/marketplace/domain.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/learning/marketplace/domain.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/learning/marketplace/flows.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/learning/marketplace/flows.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/learning/marketplace/index.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/learning/marketplace/index.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/migration.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/migration.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/admin.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/admin.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/auth-flow.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/auth-flow.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/br-helpers.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/br-helpers.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/cache.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/cache.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/database.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/database.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/deploy-safety.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/deploy-safety.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/downloads.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/downloads.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/email.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/email.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/http-client.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/http-client.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/http.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/http.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/idempotency.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/idempotency.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/logging.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/logging.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/metrics.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/metrics.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/mfa.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/mfa.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/multi-tenant.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/multi-tenant.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/observability.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/observability.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/outbox.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/outbox.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/queue-tasks.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/queue-tasks.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/realtime.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/realtime.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/security.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/security.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/sessions.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/sessions.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/storage.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/storage.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/testing.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/testing.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/uploads.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/uploads.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/utilities.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/utilities.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/webpush.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/webpush.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/websocket.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/recipes/websocket.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/reference.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/reference.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/roadmap.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/roadmap.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/tutorial.en.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/docs/tutorial.md +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/mkdocs.yml +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/scripts/extract_recipe.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/admin/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/admin/auth.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/admin/config.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/admin/forms.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/admin/router.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/admin/session.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/admin/site.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/admin/static/admin.css +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/admin/templates/base.html +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/admin/templates/dashboard.html +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/admin/templates/detail.html +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/admin/templates/form.html +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/admin/templates/list.html +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/admin/templates/login.html +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/admin/templates/logs.html +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/admin/templates/mfa.html +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/cookies.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/dependencies/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/dependencies/auth.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/handlers.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/middlewares/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/middlewares/body_size.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/middlewares/cors.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/middlewares/csrf.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/middlewares/graceful.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/middlewares/idempotency.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/middlewares/rate_limit.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/middlewares/request_id.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/oauth.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/routers/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/routers/health.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/routers/logs.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/routers/metrics.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/routers/tool_spec.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/server.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/static.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/tracing.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/api/webhooks.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/auth/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/auth/page_renderer.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/auth/router.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/auth/schemas.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/auth/service.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/auth/templates/activation.html +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/auth/templates/activation_error.html +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/auth/templates/activation_success.html +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/auth/templates/password_reset.html +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/auth/templates/password_reset_error.html +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/auth/templates/password_reset_form.html +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/auth/templates/password_reset_success.html +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cache/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cache/decorator.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cache/redis_manager.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/README.md.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/gitignore.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/main.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/pyproject.toml.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/src/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/src/api/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/src/api/dependencies/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/src/api/dependencies/auth.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/src/api/dependencies/resources.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/src/api/routers/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/src/controllers/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/src/core/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/src/core/exceptions.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/src/db/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/src/db/models/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/src/db/models/user.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/src/db/repositories/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/src/schemas/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/src/server.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/src/services/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/src/utils/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/tests/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/_templates/tests/test_smoke.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/docker_compose.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/generate.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/lint.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/new.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/src_layers.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/cli/user.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/controllers/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/controllers/base.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/core/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/core/context.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/core/enums.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/core/logging.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/db/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/db/_alembic_templates/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/db/_alembic_templates/env.py.template +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/db/alembic_hooks.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/db/connection.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/db/migrations.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/db/mixins.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/db/model.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/db/outbox.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/db/repository.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/db/slow_query.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/db/tenant.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/db/user_model.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/db/user_recovery_code_model.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/db/user_token_model.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/exceptions/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/exceptions/base.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/exceptions/conflict.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/exceptions/forbidden.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/exceptions/jwt.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/exceptions/not_found.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/exceptions/too_many_requests.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/exceptions/unauthorized.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/exceptions/upload.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/exceptions/validation.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/py.typed +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/queue/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/queue/manager.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/schemas/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/schemas/base.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/schemas/link_headers.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/schemas/logs.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/schemas/pagination.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/schemas/response.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/services/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/services/base.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/sessions/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/sessions/dependencies.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/sessions/middleware.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/sessions/router.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/sessions/schemas.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/sessions/service.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/sessions/store.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/settings/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/settings/base.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/settings/mixins.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/sse/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/sse/event_stream.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/storage/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/storage/minio_client.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/tasks/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/tasks/manager.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/tasks/scheduler.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/testing/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/testing/database.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/utils/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/utils/client_ip.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/utils/datetime.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/utils/dict.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/utils/download.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/utils/email.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/utils/http_client.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/utils/jwt.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/utils/log.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/utils/metrics.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/utils/opaque_token.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/utils/password.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/utils/regex.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/utils/storage_backends.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/utils/throttle.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/utils/totp.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/utils/upload.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/webpush/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/webpush/dispatcher.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/webpush/schemas.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/websockets/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/websockets/hub.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/websockets/router.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tempest_fastapi_sdk/websockets/schemas.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/admin/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/admin/test_auth.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/admin/test_forms.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/admin/test_logs_nav.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/admin/test_mfa.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/admin/test_router.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/admin/test_site.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/admin/test_user_model.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_body_size.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_cookies.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_cors.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_csrf.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_dependencies_auth.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_graceful.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_handlers.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_health_router.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_idempotency.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_jwt_dependency.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_logs_router.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_oauth.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_prometheus.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_rate_limit.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_request_id_middleware.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_role_dependency.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_server.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_static.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_tool_spec.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_tracing.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_webhooks.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/api/test_webhooks_rsa.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/auth/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/auth/test_mfa.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/auth/test_service.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/cache/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/cache/test_decorator.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/cache/test_redis_manager.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/cli/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/cli/test_db.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/cli/test_docker_compose.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/cli/test_generate.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/cli/test_main.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/cli/test_user.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/conftest.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/controllers/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/controllers/test_base.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/core/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/core/test_context.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/core/test_enums.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/core/test_logging.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/db/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/db/test_alembic_hooks.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/db/test_bulk_ops.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/db/test_connection.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/db/test_migrations.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/db/test_mixins.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/db/test_model.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/db/test_outbox.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/db/test_repository.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/db/test_safe_upgrade.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/db/test_slow_query.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/db/test_tenant.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/exceptions/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/exceptions/test_exceptions.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/queue/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/queue/test_manager.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/schemas/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/schemas/test_base.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/schemas/test_cursor_pagination.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/schemas/test_link_headers.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/schemas/test_pagination.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/schemas/test_response.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/services/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/services/test_base.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/sessions/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/sessions/test_sessions.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/settings/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/settings/test_base.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/settings/test_mixins.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/sse/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/sse/test_event_stream.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/storage/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/storage/test_minio_client.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/tasks/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/tasks/test_manager.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/tasks/test_scheduler.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/testing/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/testing/test_database.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/utils/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/utils/test_client_ip.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/utils/test_datetime.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/utils/test_dict.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/utils/test_download.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/utils/test_email.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/utils/test_http_client.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/utils/test_jwt.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/utils/test_lazy_extras.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/utils/test_log.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/utils/test_metrics.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/utils/test_opaque_token.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/utils/test_password.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/utils/test_regex.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/utils/test_storage_backends.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/utils/test_throttle.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/utils/test_upload.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/webpush/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/webpush/test_dispatcher.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/webpush/test_schemas.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/websockets/__init__.py +0 -0
- {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.48.0}/tests/websockets/test_hub_and_router.py +0 -0
|
@@ -5,6 +5,45 @@ 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.48.0] — 2026-06-11
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **Scaffolded services read their API `title` / `version` /
|
|
13
|
+
`description` from `.env`.** `tempest new` previously hardcoded
|
|
14
|
+
`FastAPI(title="<project>", version="0.1.0")` and the health-router
|
|
15
|
+
version in `src/api/app.py`. The scaffolded `Settings` now carries
|
|
16
|
+
`TITLE`, `DESCRIPTION` and `VERSION` fields (each with `title` /
|
|
17
|
+
`description` / `examples`), `app.py` reads them
|
|
18
|
+
(`FastAPI(title=settings.TITLE, version=settings.VERSION, ...)`,
|
|
19
|
+
`make_health_router(version=settings.VERSION)`,
|
|
20
|
+
`AdminSite(title=f"{settings.TITLE} admin")`), and `.env.example`
|
|
21
|
+
ships a documented `TITLE` / `VERSION` / `DESCRIPTION` block — so the
|
|
22
|
+
OpenAPI docs and admin header are configurable without editing code.
|
|
23
|
+
|
|
24
|
+
## [0.47.0] — 2026-06-11
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- **`tempest db seed`** — runs a project seed callable
|
|
29
|
+
(default `src.db.seeds:seed`, dotted `module:callable`, sync or async,
|
|
30
|
+
taking one `AsyncSession`) inside a managed session: commit on
|
|
31
|
+
success, rollback on error. The SDK only wires the session lifecycle;
|
|
32
|
+
the callable owns what gets inserted. Prints the row count when the
|
|
33
|
+
callable returns an `int`.
|
|
34
|
+
- **`tempest secrets rotate`** — generates fresh URL-safe secrets for
|
|
35
|
+
the keys a service signs/authenticates with (`JWT_SECRET` /
|
|
36
|
+
`TOKEN_SECRET` by default; override with `--keys`) and rewrites the
|
|
37
|
+
matching `.env` lines **in place** (existing keys replaced, missing
|
|
38
|
+
keys appended) after a `.env.bak` backup. `--print` writes nothing and
|
|
39
|
+
emits the values to stdout; `--length` sets the entropy; `--no-backup`
|
|
40
|
+
skips the backup.
|
|
41
|
+
|
|
42
|
+
### Docs
|
|
43
|
+
|
|
44
|
+
- CLI recipe (bilingual) gains **`db seed`** and **`secrets rotate`**
|
|
45
|
+
sections; README CLI section and recipes index updated.
|
|
46
|
+
|
|
8
47
|
## [0.46.0] — 2026-06-11
|
|
9
48
|
|
|
10
49
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tempest-fastapi-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.48.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
|
|
@@ -3345,7 +3345,7 @@ The skeleton matches the layered architecture documented in this README:
|
|
|
3345
3345
|
my_service/
|
|
3346
3346
|
├── main.py # one-liner → src.server.run()
|
|
3347
3347
|
├── pyproject.toml # pins tempest-fastapi-sdk + ruff/mypy/pytest
|
|
3348
|
-
├── .env.example # HOST/PORT/DATABASE_URL/JWT_SECRET/CORS_ORIGINS
|
|
3348
|
+
├── .env.example # TITLE/VERSION/HOST/PORT/DATABASE_URL/JWT_SECRET/CORS_ORIGINS
|
|
3349
3349
|
├── .gitignore
|
|
3350
3350
|
├── README.md
|
|
3351
3351
|
├── src/
|
|
@@ -3366,7 +3366,7 @@ my_service/
|
|
|
3366
3366
|
└── test_smoke.py # asserts /api/ and /health/liveness boot
|
|
3367
3367
|
```
|
|
3368
3368
|
|
|
3369
|
-
The generated `pyproject.toml` pins the current SDK version (`tempest-fastapi-sdk[auth]>=<version>` by default — change with `--extras`). The scaffolded `.env.example` uses the v0.8.0 settings naming (`SERVER_HOST`/`SERVER_PORT`/`SERVER_DEBUG`/`SERVER_RELOAD`/`LOG_LEVEL`/…), and `src/server.py` delegates to `tempest_fastapi_sdk.run_server` so uvicorn is imported lazily and tests can import the app without it. Validation rules: the project name must match `^[a-z][a-z0-9_]*$` and cannot collide with a Python keyword, so `tempest new Bad-Name` and `tempest new class` exit with code 2 before any file is written.
|
|
3369
|
+
The generated `pyproject.toml` pins the current SDK version (`tempest-fastapi-sdk[auth]>=<version>` by default — change with `--extras`). The scaffolded `.env.example` uses the v0.8.0 settings naming (`SERVER_HOST`/`SERVER_PORT`/`SERVER_DEBUG`/`SERVER_RELOAD`/`LOG_LEVEL`/…), and `src/server.py` delegates to `tempest_fastapi_sdk.run_server` so uvicorn is imported lazily and tests can import the app without it. The API `title`, `version` and `description` shown in the OpenAPI docs (and the `/admin` header) come from the `TITLE` / `VERSION` / `DESCRIPTION` settings — set them in `.env`, no code edits. Validation rules: the project name must match `^[a-z][a-z0-9_]*$` and cannot collide with a Python keyword, so `tempest new Bad-Name` and `tempest new class` exit with code 2 before any file is written.
|
|
3370
3370
|
|
|
3371
3371
|
After scaffolding:
|
|
3372
3372
|
|
|
@@ -3411,6 +3411,21 @@ tempest db upgrade <rev> # upgrade to a specific revisio
|
|
|
3411
3411
|
tempest db downgrade # roll back one step
|
|
3412
3412
|
tempest db current # print the applied revision
|
|
3413
3413
|
tempest db history -v # revisions newest → oldest, verbose
|
|
3414
|
+
tempest db seed # run src.db.seeds:seed in one session
|
|
3415
|
+
tempest db seed --seed src.db.fixtures:demo # custom seed callable
|
|
3416
|
+
```
|
|
3417
|
+
|
|
3418
|
+
`tempest db seed` runs a project seed callable (default `src.db.seeds:seed`, sync or async, taking one `AsyncSession`) inside a managed session — commit on success, rollback on error.
|
|
3419
|
+
|
|
3420
|
+
#### Secrets — `tempest secrets`
|
|
3421
|
+
|
|
3422
|
+
Generates and rotates application secrets (`JWT_SECRET` / `TOKEN_SECRET` by default), rewriting the matching `.env` lines in place after a `.env.bak` backup; `--print` writes nothing and emits the values to stdout.
|
|
3423
|
+
|
|
3424
|
+
```bash
|
|
3425
|
+
tempest secrets rotate # rotate JWT_SECRET + TOKEN_SECRET in .env
|
|
3426
|
+
tempest secrets rotate --print # just print, write nothing
|
|
3427
|
+
tempest secrets rotate --keys JWT_SECRET,SESSION_SECRET --env .env.prod
|
|
3428
|
+
tempest secrets rotate --length 64 --no-backup
|
|
3414
3429
|
```
|
|
3415
3430
|
|
|
3416
3431
|
#### Users — `tempest user`
|
|
@@ -3243,7 +3243,7 @@ The skeleton matches the layered architecture documented in this README:
|
|
|
3243
3243
|
my_service/
|
|
3244
3244
|
├── main.py # one-liner → src.server.run()
|
|
3245
3245
|
├── pyproject.toml # pins tempest-fastapi-sdk + ruff/mypy/pytest
|
|
3246
|
-
├── .env.example # HOST/PORT/DATABASE_URL/JWT_SECRET/CORS_ORIGINS
|
|
3246
|
+
├── .env.example # TITLE/VERSION/HOST/PORT/DATABASE_URL/JWT_SECRET/CORS_ORIGINS
|
|
3247
3247
|
├── .gitignore
|
|
3248
3248
|
├── README.md
|
|
3249
3249
|
├── src/
|
|
@@ -3264,7 +3264,7 @@ my_service/
|
|
|
3264
3264
|
└── test_smoke.py # asserts /api/ and /health/liveness boot
|
|
3265
3265
|
```
|
|
3266
3266
|
|
|
3267
|
-
The generated `pyproject.toml` pins the current SDK version (`tempest-fastapi-sdk[auth]>=<version>` by default — change with `--extras`). The scaffolded `.env.example` uses the v0.8.0 settings naming (`SERVER_HOST`/`SERVER_PORT`/`SERVER_DEBUG`/`SERVER_RELOAD`/`LOG_LEVEL`/…), and `src/server.py` delegates to `tempest_fastapi_sdk.run_server` so uvicorn is imported lazily and tests can import the app without it. Validation rules: the project name must match `^[a-z][a-z0-9_]*$` and cannot collide with a Python keyword, so `tempest new Bad-Name` and `tempest new class` exit with code 2 before any file is written.
|
|
3267
|
+
The generated `pyproject.toml` pins the current SDK version (`tempest-fastapi-sdk[auth]>=<version>` by default — change with `--extras`). The scaffolded `.env.example` uses the v0.8.0 settings naming (`SERVER_HOST`/`SERVER_PORT`/`SERVER_DEBUG`/`SERVER_RELOAD`/`LOG_LEVEL`/…), and `src/server.py` delegates to `tempest_fastapi_sdk.run_server` so uvicorn is imported lazily and tests can import the app without it. The API `title`, `version` and `description` shown in the OpenAPI docs (and the `/admin` header) come from the `TITLE` / `VERSION` / `DESCRIPTION` settings — set them in `.env`, no code edits. Validation rules: the project name must match `^[a-z][a-z0-9_]*$` and cannot collide with a Python keyword, so `tempest new Bad-Name` and `tempest new class` exit with code 2 before any file is written.
|
|
3268
3268
|
|
|
3269
3269
|
After scaffolding:
|
|
3270
3270
|
|
|
@@ -3309,6 +3309,21 @@ tempest db upgrade <rev> # upgrade to a specific revisio
|
|
|
3309
3309
|
tempest db downgrade # roll back one step
|
|
3310
3310
|
tempest db current # print the applied revision
|
|
3311
3311
|
tempest db history -v # revisions newest → oldest, verbose
|
|
3312
|
+
tempest db seed # run src.db.seeds:seed in one session
|
|
3313
|
+
tempest db seed --seed src.db.fixtures:demo # custom seed callable
|
|
3314
|
+
```
|
|
3315
|
+
|
|
3316
|
+
`tempest db seed` runs a project seed callable (default `src.db.seeds:seed`, sync or async, taking one `AsyncSession`) inside a managed session — commit on success, rollback on error.
|
|
3317
|
+
|
|
3318
|
+
#### Secrets — `tempest secrets`
|
|
3319
|
+
|
|
3320
|
+
Generates and rotates application secrets (`JWT_SECRET` / `TOKEN_SECRET` by default), rewriting the matching `.env` lines in place after a `.env.bak` backup; `--print` writes nothing and emits the values to stdout.
|
|
3321
|
+
|
|
3322
|
+
```bash
|
|
3323
|
+
tempest secrets rotate # rotate JWT_SECRET + TOKEN_SECRET in .env
|
|
3324
|
+
tempest secrets rotate --print # just print, write nothing
|
|
3325
|
+
tempest secrets rotate --keys JWT_SECRET,SESSION_SECRET --env .env.prod
|
|
3326
|
+
tempest secrets rotate --length 64 --no-backup
|
|
3312
3327
|
```
|
|
3313
3328
|
|
|
3314
3329
|
#### Users — `tempest user`
|
|
@@ -39,7 +39,7 @@ The skeleton matches the layered architecture documented in this README:
|
|
|
39
39
|
my_service/
|
|
40
40
|
├── main.py # one-liner → src.server.run()
|
|
41
41
|
├── pyproject.toml # pins tempest-fastapi-sdk + ruff/mypy/pytest
|
|
42
|
-
├── .env.example # HOST/PORT/DATABASE_URL/JWT_SECRET/CORS_ORIGINS
|
|
42
|
+
├── .env.example # TITLE/VERSION/HOST/PORT/DATABASE_URL/JWT_SECRET/CORS_ORIGINS
|
|
43
43
|
├── docker-compose.yaml # services keyed to the chosen extras
|
|
44
44
|
├── .gitignore
|
|
45
45
|
├── README.md
|
|
@@ -63,6 +63,21 @@ my_service/
|
|
|
63
63
|
|
|
64
64
|
The generated `pyproject.toml` pins the current SDK version (`tempest-fastapi-sdk[auth,admin]>=<version>` by default — change with `--extras`). The scaffolded `.env.example` uses the v0.8.0 settings naming (`SERVER_HOST`/`SERVER_PORT`/`SERVER_DEBUG`/`SERVER_RELOAD`/`LOG_LEVEL`/…), and `src/server.py` delegates to `tempest_fastapi_sdk.run_server` so uvicorn is imported lazily and tests can import the app without it. Validation rules: the project name must match `^[a-z][a-z0-9_]*$` and cannot collide with a Python keyword, so `tempest new Bad-Name` and `tempest new class` exit with code 2 before any file is written.
|
|
65
65
|
|
|
66
|
+
!!! tip "API title / version come from `.env`"
|
|
67
|
+
Since v0.48.0 the scaffolded `Settings` carries `TITLE`, `VERSION`
|
|
68
|
+
and `DESCRIPTION`, and `src/api/app.py` consumes them
|
|
69
|
+
(`FastAPI(title=settings.TITLE, version=settings.VERSION,
|
|
70
|
+
description=settings.DESCRIPTION)`, `make_health_router(version=
|
|
71
|
+
settings.VERSION)` and `AdminSite(title=f"{settings.TITLE} admin")`).
|
|
72
|
+
Tune the title shown in Swagger/ReDoc and the `/admin` header from
|
|
73
|
+
`.env`, no code edits:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
TITLE=My API
|
|
77
|
+
VERSION=1.2.0
|
|
78
|
+
DESCRIPTION=Payments API for product X.
|
|
79
|
+
```
|
|
80
|
+
|
|
66
81
|
### Extras-driven `docker-compose.yaml`
|
|
67
82
|
|
|
68
83
|
Since v0.25.0 the scaffold generates a `docker-compose.yaml` carrying **only** the supporting services the chosen extras actually need — no ZooKeeper, no Kafka, nothing you won't use.
|
|
@@ -140,8 +155,29 @@ tempest db downgrade <rev> # roll back to a specific revis
|
|
|
140
155
|
tempest db current # print the applied revision
|
|
141
156
|
tempest db history # revisions newest → oldest
|
|
142
157
|
tempest db history -v # with full message body
|
|
158
|
+
tempest db seed # runs src.db.seeds:seed
|
|
159
|
+
tempest db seed --seed src.db.fixtures:demo # custom callable
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### Seed the database — `tempest db seed`
|
|
163
|
+
|
|
164
|
+
Runs a project seed callable inside a managed session (commit on success, rollback on error). The callable takes a positional `AsyncSession` and may be sync or async; what it inserts is up to you — the SDK only wires the session lifecycle. Defaults to importing `src.db.seeds:seed`.
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
168
|
+
|
|
169
|
+
from src.db.models import CategoryModel
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
async def seed(session: AsyncSession) -> int:
|
|
173
|
+
"""Seed initial categories. Return the count (optional)."""
|
|
174
|
+
session.add_all([CategoryModel(name="Books"), CategoryModel(name="Games")])
|
|
175
|
+
await session.flush()
|
|
176
|
+
return 2
|
|
143
177
|
```
|
|
144
178
|
|
|
179
|
+
When the callable returns an `int`, the CLI prints the count: `Seeded via src.db.seeds:seed (2 rows).`
|
|
180
|
+
|
|
145
181
|
### Users — `tempest user`
|
|
146
182
|
|
|
147
183
|
Seed and list users using the project's concrete ``UserModel`` (default ``src.db.models:UserModel``). Bootstraps the first admin without manual SQL.
|
|
@@ -180,6 +216,27 @@ tempest user list --admin # admins only
|
|
|
180
216
|
|
|
181
217
|
``DATABASE_URL`` resolves the same way as ``tempest db`` (env var > settings > alembic.ini).
|
|
182
218
|
|
|
219
|
+
### Secrets — `tempest secrets`
|
|
220
|
+
|
|
221
|
+
Generate and rotate application secrets (`JWT_SECRET` / `TOKEN_SECRET` by default), rewriting the matching `.env` lines **in place** — backing up the old file first — and leaving every other line untouched.
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
# Rotate JWT_SECRET and TOKEN_SECRET in .env (writes .env.bak)
|
|
225
|
+
tempest secrets rotate
|
|
226
|
+
|
|
227
|
+
# Just print the new values (writes nothing) — pipe into a secret manager
|
|
228
|
+
tempest secrets rotate --print
|
|
229
|
+
|
|
230
|
+
# Custom keys and file
|
|
231
|
+
tempest secrets rotate --keys JWT_SECRET,SESSION_SECRET --env .env.prod
|
|
232
|
+
|
|
233
|
+
# More entropy, no backup
|
|
234
|
+
tempest secrets rotate --length 64 --no-backup
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
!!! warning
|
|
238
|
+
Rotating `JWT_SECRET` invalidates every token signed with the old value: users are logged out and pending reset/activation links stop working. Rotate during a maintenance window and restart the service to load the new values.
|
|
239
|
+
|
|
183
240
|
### Regenerating `docker-compose.yaml` in an existing project
|
|
184
241
|
|
|
185
242
|
When you change installed extras (`uv add "tempest-fastapi-sdk[minio]"`) or the SDK bumps image versions, regenerate with:
|
|
@@ -39,7 +39,7 @@ O esqueleto casa com a arquitetura em camadas documentada neste README:
|
|
|
39
39
|
my_service/
|
|
40
40
|
├── main.py # one-liner → src.server.run()
|
|
41
41
|
├── pyproject.toml # fixa tempest-fastapi-sdk + ruff/mypy/pytest
|
|
42
|
-
├── .env.example # HOST/PORT/DATABASE_URL/JWT_SECRET/CORS_ORIGINS
|
|
42
|
+
├── .env.example # TITLE/VERSION/HOST/PORT/DATABASE_URL/JWT_SECRET/CORS_ORIGINS
|
|
43
43
|
├── docker-compose.yaml # serviços baseados nos extras escolhidos
|
|
44
44
|
├── .gitignore
|
|
45
45
|
├── README.md
|
|
@@ -63,6 +63,21 @@ my_service/
|
|
|
63
63
|
|
|
64
64
|
O `pyproject.toml` gerado fixa a versão atual do SDK (`tempest-fastapi-sdk[auth,admin]>=<versão>` por padrão — mude com `--extras`). O `.env.example` criado usa a nomenclatura de settings da v0.8.0 (`SERVER_HOST`/`SERVER_PORT`/`SERVER_DEBUG`/`SERVER_RELOAD`/`LOG_LEVEL`/…), e `src/server.py` delega a `tempest_fastapi_sdk.run_server` para que o uvicorn seja importado de forma preguiçosa e os testes possam importar o app sem ele. Regras de validação: o nome do projeto deve casar com `^[a-z][a-z0-9_]*$` e não pode colidir com uma palavra-chave do Python, então `tempest new Bad-Name` e `tempest new class` saem com código 2 antes de qualquer arquivo ser escrito.
|
|
65
65
|
|
|
66
|
+
!!! tip "Título / versão da API vêm do `.env`"
|
|
67
|
+
A partir da v0.48.0 o `Settings` scaffoldado carrega `TITLE`,
|
|
68
|
+
`VERSION` e `DESCRIPTION`, e `src/api/app.py` os consome
|
|
69
|
+
(`FastAPI(title=settings.TITLE, version=settings.VERSION,
|
|
70
|
+
description=settings.DESCRIPTION)`, `make_health_router(version=
|
|
71
|
+
settings.VERSION)` e `AdminSite(title=f"{settings.TITLE} admin")`).
|
|
72
|
+
Ajuste o título mostrado no Swagger/ReDoc e no header do `/admin`
|
|
73
|
+
direto no `.env`, sem editar código:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
TITLE=Minha API
|
|
77
|
+
VERSION=1.2.0
|
|
78
|
+
DESCRIPTION=API de pagamentos do produto X.
|
|
79
|
+
```
|
|
80
|
+
|
|
66
81
|
### `docker-compose.yaml` baseado nos extras
|
|
67
82
|
|
|
68
83
|
Desde a v0.25.0 o scaffold gera um `docker-compose.yaml` com **apenas** os serviços que os extras escolhidos precisam — sem ZooKeeper, Kafka ou qualquer outra coisa que você não vai usar.
|
|
@@ -188,8 +203,29 @@ tempest db downgrade <rev> # rollback até rev específico
|
|
|
188
203
|
tempest db current # imprime revision aplicado
|
|
189
204
|
tempest db history # histórico de revisions
|
|
190
205
|
tempest db history -v # com message body completo
|
|
206
|
+
tempest db seed # roda src.db.seeds:seed
|
|
207
|
+
tempest db seed --seed src.db.fixtures:demo # callable customizado
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
#### Popular o banco — `tempest db seed`
|
|
211
|
+
|
|
212
|
+
Roda um callable de seed do projeto dentro de uma sessão gerenciada (commit no sucesso, rollback no erro). O callable recebe uma `AsyncSession` posicional e pode ser sync ou async; o que ele insere é decisão sua — o SDK só cuida do ciclo de vida da sessão. Por padrão importa `src.db.seeds:seed`.
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
216
|
+
|
|
217
|
+
from src.db.models import CategoryModel
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
async def seed(session: AsyncSession) -> int:
|
|
221
|
+
"""Popula categorias iniciais. Retorna a contagem (opcional)."""
|
|
222
|
+
session.add_all([CategoryModel(name="Livros"), CategoryModel(name="Games")])
|
|
223
|
+
await session.flush()
|
|
224
|
+
return 2
|
|
191
225
|
```
|
|
192
226
|
|
|
227
|
+
Quando o callable devolve um `int`, a CLI mostra a contagem: `Seeded via src.db.seeds:seed (2 rows).`
|
|
228
|
+
|
|
193
229
|
### Usuários — `tempest user`
|
|
194
230
|
|
|
195
231
|
Insere/lista usuários direto no banco usando o `UserModel` concreto do projeto (default `src.db.models:UserModel`). Útil pra bootstrapear o primeiro admin sem rodar SQL manual.
|
|
@@ -228,6 +264,27 @@ tempest user list --admin # só admins
|
|
|
228
264
|
|
|
229
265
|
Resolução do `DATABASE_URL` igual ao `tempest db` (env var > settings > alembic.ini).
|
|
230
266
|
|
|
267
|
+
### Segredos — `tempest secrets`
|
|
268
|
+
|
|
269
|
+
Gera e rotaciona os segredos da aplicação (`JWT_SECRET` / `TOKEN_SECRET` por padrão), reescrevendo as linhas correspondentes no `.env` **no lugar** — fazendo backup do arquivo antigo antes — e deixando as outras linhas intactas.
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
# Rotaciona JWT_SECRET e TOKEN_SECRET no .env (gera .env.bak)
|
|
273
|
+
tempest secrets rotate
|
|
274
|
+
|
|
275
|
+
# Só imprime os novos valores (não escreve nada) — pra pipar num secret manager
|
|
276
|
+
tempest secrets rotate --print
|
|
277
|
+
|
|
278
|
+
# Chaves e arquivo customizados
|
|
279
|
+
tempest secrets rotate --keys JWT_SECRET,SESSION_SECRET --env .env.prod
|
|
280
|
+
|
|
281
|
+
# Mais entropia, sem backup
|
|
282
|
+
tempest secrets rotate --length 64 --no-backup
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
!!! warning
|
|
286
|
+
Rotacionar `JWT_SECRET` invalida todo token assinado com o valor antigo: usuários são deslogados e links de reset/ativação pendentes param de funcionar. Rotacione numa janela de manutenção e reinicie o serviço pra carregar os novos valores.
|
|
287
|
+
|
|
231
288
|
#### Gates de qualidade
|
|
232
289
|
|
|
233
290
|
Os comandos de lint chamam a ferramenta do projeto. Eles procuram o executável no `PATH` primeiro e, caso contrário, caem para `uv run <tool>` para que um virtualenv local do projeto funcione sem ativação manual.
|
|
@@ -27,7 +27,7 @@ Bite-sized "I want to wire X" walkthroughs. Each page starts with **what problem
|
|
|
27
27
|
| **[Utilities »](utilities.md)** | `utcnow`/`to_utc`, `modify_dict`, `get_client_ip`, opaque tokens (`generate_opaque_token`) |
|
|
28
28
|
| **[Testing »](testing.md)** | `test_session`, `test_database`, in-memory SQLite, pytest fixtures |
|
|
29
29
|
| **[Safe deploys »](deploy-safety.md)** | `AlembicHelper.safe_upgrade` (blocks DROPs), `GracefulShutdownMiddleware` |
|
|
30
|
-
| **[CLI »](cli.md)** | `tempest new` / `lint` / `fix` / `format` / `type` / `test` / `check` |
|
|
30
|
+
| **[CLI »](cli.md)** | `tempest new` / `db` (+ `seed`) / `user` / `secrets rotate` / `lint` / `fix` / `format` / `type` / `test` / `check` |
|
|
31
31
|
| **[Security »](security.md)** | `AttemptThrottle`, opaque-token helpers, `HardenedStaticFiles`, security headers |
|
|
32
32
|
| **[Brazilian helpers »](br-helpers.md)** | CPF / CNPJ / CEP / phone validation + normalization |
|
|
33
33
|
|
|
@@ -27,7 +27,7 @@ Passo a passo curtos no estilo "quero conectar X". Cada página começa com **qu
|
|
|
27
27
|
| **[Utilitários »](utilities.md)** | `utcnow`/`to_utc`, `modify_dict`, `get_client_ip`, tokens opacos (`generate_opaque_token`) |
|
|
28
28
|
| **[Testes »](testing.md)** | `test_session`, `test_database`, SQLite em memória, fixtures pytest |
|
|
29
29
|
| **[Deploy seguro »](deploy-safety.md)** | `AlembicHelper.safe_upgrade` (barra DROPs), `GracefulShutdownMiddleware` |
|
|
30
|
-
| **[CLI »](cli.md)** | `tempest new` / `lint` / `fix` / `format` / `type` / `test` / `check` |
|
|
30
|
+
| **[CLI »](cli.md)** | `tempest new` / `db` (+ `seed`) / `user` / `secrets rotate` / `lint` / `fix` / `format` / `type` / `test` / `check` |
|
|
31
31
|
| **[Segurança »](security.md)** | `AttemptThrottle`, helpers de token opaco, `HardenedStaticFiles`, headers de segurança |
|
|
32
32
|
| **[Helpers brasileiros »](br-helpers.md)** | validação + normalização de CPF / CNPJ / CEP / telefone |
|
|
33
33
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tempest-fastapi-sdk"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.48.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"
|
|
@@ -48,8 +48,9 @@ def create_app() -> FastAPI:
|
|
|
48
48
|
FastAPI: A fully wired application ready for uvicorn.
|
|
49
49
|
"""
|
|
50
50
|
app = FastAPI(
|
|
51
|
-
title=
|
|
52
|
-
|
|
51
|
+
title=settings.TITLE,
|
|
52
|
+
description=settings.DESCRIPTION,
|
|
53
|
+
version=settings.VERSION,
|
|
53
54
|
lifespan=lifespan,
|
|
54
55
|
)
|
|
55
56
|
|
|
@@ -58,7 +59,7 @@ def create_app() -> FastAPI:
|
|
|
58
59
|
register_exception_handlers(app)
|
|
59
60
|
|
|
60
61
|
app.include_router(
|
|
61
|
-
make_health_router(db=db, version=
|
|
62
|
+
make_health_router(db=db, version=settings.VERSION),
|
|
62
63
|
)
|
|
63
64
|
app.include_router(
|
|
64
65
|
make_logs_router(
|
|
@@ -72,7 +73,7 @@ def create_app() -> FastAPI:
|
|
|
72
73
|
# Alembic migration:
|
|
73
74
|
# uv run alembic upgrade head
|
|
74
75
|
# uv run python -c "..." # set hashed_password + is_admin=True
|
|
75
|
-
admin_site = AdminSite(title="
|
|
76
|
+
admin_site = AdminSite(title=f"{settings.TITLE} admin")
|
|
76
77
|
admin_site.register(AdminModel(model=UserModel))
|
|
77
78
|
app.include_router(
|
|
78
79
|
make_admin_router(
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Application settings — composed from SDK mixins + service overrides."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pydantic import Field
|
|
6
|
+
|
|
7
|
+
from tempest_fastapi_sdk import (
|
|
8
|
+
BaseAppSettings,
|
|
9
|
+
CORSSettings,
|
|
10
|
+
DatabaseSettings,
|
|
11
|
+
JWTSettings,
|
|
12
|
+
LogSettings,
|
|
13
|
+
ServerSettings,
|
|
14
|
+
TokenSettings,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Settings(
|
|
19
|
+
ServerSettings,
|
|
20
|
+
LogSettings,
|
|
21
|
+
DatabaseSettings,
|
|
22
|
+
JWTSettings,
|
|
23
|
+
TokenSettings,
|
|
24
|
+
CORSSettings,
|
|
25
|
+
BaseAppSettings,
|
|
26
|
+
):
|
|
27
|
+
"""Service-wide settings backed by the ``.env`` file.
|
|
28
|
+
|
|
29
|
+
Attributes:
|
|
30
|
+
TITLE (str): API title shown in the OpenAPI docs and the admin
|
|
31
|
+
panel header. Override via the ``TITLE`` env var.
|
|
32
|
+
DESCRIPTION (str): Optional API description shown in the OpenAPI
|
|
33
|
+
docs. Override via the ``DESCRIPTION`` env var.
|
|
34
|
+
VERSION (str): Version surfaced in the OpenAPI docs and on the
|
|
35
|
+
health router. Override via the ``VERSION`` env var.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
TITLE: str = Field(
|
|
39
|
+
default="__PROJECT_NAME__",
|
|
40
|
+
title="API title",
|
|
41
|
+
description="Shown in the OpenAPI docs and the admin panel header.",
|
|
42
|
+
examples=["__PROJECT_NAME__"],
|
|
43
|
+
)
|
|
44
|
+
DESCRIPTION: str = Field(
|
|
45
|
+
default="",
|
|
46
|
+
title="API description",
|
|
47
|
+
description="Optional summary shown in the OpenAPI docs.",
|
|
48
|
+
examples=["Backend API for __PROJECT_NAME__."],
|
|
49
|
+
)
|
|
50
|
+
VERSION: str = Field(
|
|
51
|
+
default="0.1.0",
|
|
52
|
+
title="API version",
|
|
53
|
+
description="Surfaced in the OpenAPI docs and the health router.",
|
|
54
|
+
examples=["0.1.0"],
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
settings = Settings()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
__all__: list[str] = ["Settings", "settings"]
|
|
@@ -20,12 +20,21 @@ stripped automatically before Alembic runs.
|
|
|
20
20
|
|
|
21
21
|
from __future__ import annotations
|
|
22
22
|
|
|
23
|
+
import asyncio
|
|
24
|
+
import importlib
|
|
25
|
+
import inspect
|
|
23
26
|
import os
|
|
24
27
|
import sys
|
|
25
28
|
from pathlib import Path
|
|
29
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
26
30
|
|
|
27
31
|
import typer
|
|
28
32
|
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from collections.abc import Callable
|
|
35
|
+
|
|
36
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
37
|
+
|
|
29
38
|
|
|
30
39
|
def _resolve_database_url(explicit: str | None) -> str | None:
|
|
31
40
|
"""Pick the DB URL using the documented priority order.
|
|
@@ -95,6 +104,70 @@ def _helper(
|
|
|
95
104
|
return AlembicHelper(str(ini), db_url=database_url)
|
|
96
105
|
|
|
97
106
|
|
|
107
|
+
def _load_seed_callable(spec: str) -> Callable[[AsyncSession], Any]:
|
|
108
|
+
"""Import a ``module.path:callable`` seed entry point.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
spec (str): Dotted spec ``"module.path:callable"``.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Callable[[AsyncSession], Any]: The seed callable (sync or async).
|
|
115
|
+
|
|
116
|
+
Raises:
|
|
117
|
+
typer.Exit: When the spec is malformed, the import fails, or the
|
|
118
|
+
attribute is not callable.
|
|
119
|
+
"""
|
|
120
|
+
module_path, _, attr = spec.partition(":")
|
|
121
|
+
if not module_path or not attr:
|
|
122
|
+
typer.echo(
|
|
123
|
+
f"error: --seed must be 'module.path:callable', got {spec!r}",
|
|
124
|
+
err=True,
|
|
125
|
+
)
|
|
126
|
+
raise typer.Exit(2)
|
|
127
|
+
cwd = str(Path.cwd())
|
|
128
|
+
if cwd not in sys.path:
|
|
129
|
+
sys.path.insert(0, cwd)
|
|
130
|
+
try:
|
|
131
|
+
module = importlib.import_module(module_path)
|
|
132
|
+
except ImportError as exc:
|
|
133
|
+
typer.echo(f"error: cannot import {module_path!r}: {exc}", err=True)
|
|
134
|
+
raise typer.Exit(2) from exc
|
|
135
|
+
seed: object = getattr(module, attr, None)
|
|
136
|
+
if not callable(seed):
|
|
137
|
+
typer.echo(
|
|
138
|
+
f"error: {spec} is not callable (got {type(seed).__name__}).",
|
|
139
|
+
err=True,
|
|
140
|
+
)
|
|
141
|
+
raise typer.Exit(2)
|
|
142
|
+
return cast("Callable[[AsyncSession], Any]", seed)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
async def _run_seed(database_url: str, seed: Callable[[AsyncSession], Any]) -> Any:
|
|
146
|
+
"""Open one managed session and run the seed callable in it.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
database_url (str): The resolved database URL.
|
|
150
|
+
seed (Callable[[AsyncSession], Any]): The seed callable; awaited
|
|
151
|
+
when it returns a coroutine.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Any: Whatever the seed callable returns (e.g. a row count), or
|
|
155
|
+
``None``.
|
|
156
|
+
"""
|
|
157
|
+
from tempest_fastapi_sdk import AsyncDatabaseManager
|
|
158
|
+
|
|
159
|
+
manager = AsyncDatabaseManager(database_url)
|
|
160
|
+
await manager.connect()
|
|
161
|
+
try:
|
|
162
|
+
async with manager.get_session_context() as session:
|
|
163
|
+
result = seed(session)
|
|
164
|
+
if inspect.isawaitable(result):
|
|
165
|
+
result = await result
|
|
166
|
+
return result
|
|
167
|
+
finally:
|
|
168
|
+
await manager.disconnect()
|
|
169
|
+
|
|
170
|
+
|
|
98
171
|
db_app: typer.Typer = typer.Typer(
|
|
99
172
|
name="db",
|
|
100
173
|
help="Alembic migration helpers (create / apply / inspect).",
|
|
@@ -242,6 +315,46 @@ def db_current(
|
|
|
242
315
|
typer.echo(current or "(no revision applied)")
|
|
243
316
|
|
|
244
317
|
|
|
318
|
+
@db_app.command("seed")
|
|
319
|
+
def db_seed(
|
|
320
|
+
seed_spec: str = typer.Option(
|
|
321
|
+
"src.db.seeds:seed",
|
|
322
|
+
"--seed",
|
|
323
|
+
"-s",
|
|
324
|
+
help=(
|
|
325
|
+
"Dotted 'module.path:callable' to run. The callable receives "
|
|
326
|
+
"one positional AsyncSession and may be sync or async. Defaults "
|
|
327
|
+
"to the scaffolded 'src.db.seeds:seed'."
|
|
328
|
+
),
|
|
329
|
+
),
|
|
330
|
+
database_url: str | None = typer.Option(
|
|
331
|
+
None,
|
|
332
|
+
"--database-url",
|
|
333
|
+
help="Override DATABASE_URL for this run.",
|
|
334
|
+
),
|
|
335
|
+
) -> None:
|
|
336
|
+
"""Run a project seed callable inside one managed session.
|
|
337
|
+
|
|
338
|
+
Builds an :class:`AsyncDatabaseManager` from the resolved URL, opens
|
|
339
|
+
a session (committed on success, rolled back on error), and invokes
|
|
340
|
+
the dotted callable with it. The callable owns what gets inserted —
|
|
341
|
+
the SDK only wires the session lifecycle so seeding looks the same
|
|
342
|
+
across every service.
|
|
343
|
+
"""
|
|
344
|
+
url = _resolve_database_url(database_url)
|
|
345
|
+
if url is None:
|
|
346
|
+
typer.echo(
|
|
347
|
+
"error: no database URL. Pass --database-url, set DATABASE_URL, "
|
|
348
|
+
"or run inside a project with src/core/settings.py.",
|
|
349
|
+
err=True,
|
|
350
|
+
)
|
|
351
|
+
raise typer.Exit(2)
|
|
352
|
+
seed_callable = _load_seed_callable(seed_spec)
|
|
353
|
+
result = asyncio.run(_run_seed(url, seed_callable))
|
|
354
|
+
suffix = f" ({result} rows)" if isinstance(result, int) else ""
|
|
355
|
+
typer.echo(f"Seeded via {seed_spec}{suffix}.")
|
|
356
|
+
|
|
357
|
+
|
|
245
358
|
@db_app.command("history")
|
|
246
359
|
def db_history(
|
|
247
360
|
verbose: bool = typer.Option(
|
|
@@ -14,6 +14,7 @@ from tempest_fastapi_sdk.cli import generate as generate_module
|
|
|
14
14
|
from tempest_fastapi_sdk.cli import lint as lint_module
|
|
15
15
|
from tempest_fastapi_sdk.cli import new as new_module
|
|
16
16
|
from tempest_fastapi_sdk.cli.db import db_app
|
|
17
|
+
from tempest_fastapi_sdk.cli.secrets import secrets_app
|
|
17
18
|
from tempest_fastapi_sdk.cli.user import user_app
|
|
18
19
|
|
|
19
20
|
# Typer >= 0.13 vendors its own copy of Click under ``typer._click``, so a
|
|
@@ -136,6 +137,7 @@ app: typer.Typer = typer.Typer(
|
|
|
136
137
|
)
|
|
137
138
|
app.add_typer(db_app, name="db")
|
|
138
139
|
app.add_typer(user_app, name="user")
|
|
140
|
+
app.add_typer(secrets_app, name="secrets")
|
|
139
141
|
|
|
140
142
|
|
|
141
143
|
def _print_version(value: bool) -> None:
|