fastapi-rtk 2.11.4__tar.gz → 2.12.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.
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/PKG-INFO +1 -1
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/__init__.py +17 -0
- fastapi_rtk-2.12.0/fastapi_rtk/_version.py +1 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/auth.py +22 -1
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/bases/__init__.py +3 -0
- fastapi_rtk-2.12.0/fastapi_rtk/bases/cache.py +85 -0
- fastapi_rtk-2.12.0/fastapi_rtk/caches/__init__.py +10 -0
- fastapi_rtk-2.12.0/fastapi_rtk/caches/cache.py +78 -0
- fastapi_rtk-2.12.0/fastapi_rtk/caches/serializers.py +95 -0
- fastapi_rtk-2.12.0/fastapi_rtk/caches/storages.py +78 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/db.py +26 -2
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/dependencies.py +5 -1
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/fastapi_react_toolkit.py +28 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/globals.py +14 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/manager.py +45 -4
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/apis.py +62 -1
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/models.py +7 -0
- fastapi_rtk-2.12.0/fastapi_rtk/security/sqla/user_cache.py +178 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/setting.py +15 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/test_auth.py +151 -26
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/security/sqla/test_apis.py +63 -0
- fastapi_rtk-2.12.0/tests/unit/test_caches.py +166 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_db.py +56 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_dependencies.py +24 -4
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_manager.py +85 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_setting.py +2 -0
- fastapi_rtk-2.12.0/tests/unit/test_user_cache.py +199 -0
- fastapi_rtk-2.11.4/fastapi_rtk/_version.py +0 -1
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/.gitignore +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/LICENSE +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/README.md +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/base_api.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_add_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_bulk_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_columns_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_delete_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_download_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_edit_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_file_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_info_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_list_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_params.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_show_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_typed_base.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_upload_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/apis/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/apis/info.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/apis/license.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/hashers/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/hashers/pbkdf2.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/hashers/scrypt.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/ldap.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/password_helpers/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/password_helpers/fab.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/strategies/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/strategies/config.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/strategies/db.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/strategies/jwt.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/generic/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/generic/column.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/generic/db.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/generic/exceptions.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/generic/filters.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/generic/interface.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/generic/model.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/generic/session.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/column.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/db.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/exceptions.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/audit/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/audit/_decorators_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/audit/_lifecycle_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/audit/_logger.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/audit/_processing_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/audit/audit.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/audit/types.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/filters.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/geometry_converter.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/filters.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/interface.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/model.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/session.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/bases/db.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/bases/file_manager.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/bases/filter.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/bases/interface.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/bases/model.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/bases/session.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/cli.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/_security_crypto.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/db/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/db/templates/fastapi/README +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/db/templates/fastapi/alembic.ini.mako +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/db/templates/fastapi/env.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/db/templates/fastapi/script.py.mako +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/README +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/alembic.ini.mako +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/env.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/script.py.mako +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/export.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/security.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/translate.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/const.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/decorators.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/types.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/utils.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/config.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/const.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/decorators.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/exceptions.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/file_managers/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/file_managers/file_manager.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/file_managers/image_manager.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/file_managers/s3_file_manager.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/file_managers/s3_image_manager.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/filters.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/babel/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/babel/cli.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/babel/config.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/babel.cfg +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/lazy_text.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/messages.pot +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.mo +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.po +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.mo +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.po +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/middlewares.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/mixins.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/models.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/py.typed +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/registry.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/routers.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/schemas.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/security_manager/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/security_manager/_association_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/security_manager/_builtin_role_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/security_manager/_cleanup_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/security_manager/_exception_filters.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/security_manager/_export.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/security_manager/_import.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/security_manager/_role_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/security_manager/_user_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/types.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/async_task_runner.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/class_factory.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/csv_json_converter.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/deep_merge.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/extender_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/flask_appbuilder_utils.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/formatter.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/hooks.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/lazy.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/merge_schema.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/multiple_async_contexts.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/pydantic.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/run_utils.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/self_dependencies.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/smartdefaultdict.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/sqla.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/timezone.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/update_signature.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/use_default_when_none.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/version.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/pyproject.toml +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/conftest.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/docker-compose.gis.yml +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/api/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/api/test_model_rest_api.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/apis/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/apis/test_info_callable_schema.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/apis/test_info_contract.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/auth/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/auth/test_auth_flow.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/auth/test_oauth.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/conftest.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/file_managers/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/file_managers/test_file_routes.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/security/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/security/sqla/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/security/sqla/test_security_manager.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/test_app_smoke.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/api/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/api/conftest.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/api/model_rest_api/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/api/model_rest_api/test_columns.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/api/model_rest_api/test_file_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/api/model_rest_api/test_init.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/api/model_rest_api/test_typed_base_consistency.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/api/test_base_api.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/apis/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/apis/test_info.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/apis/test_license.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/hashers/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/hashers/test_pbkdf2.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/hashers/test_scrypt.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/password_helpers/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/password_helpers/test_fab.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/strategies/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/strategies/test_config.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/strategies/test_db.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/strategies/test_jwt.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/generic/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/generic/test_column.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/generic/test_db.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/generic/test_exceptions.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/generic/test_filters.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/generic/test_interface.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/generic/test_model.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/generic/test_session.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/conftest.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/test_audit.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/test_audit_types.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/test_column.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/test_db.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/test_filters.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/test_geoalchemy2.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/test_interface.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/test_model.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/test_session.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/bases/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/bases/test_db.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/bases/test_file_manager.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/bases/test_filter.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/bases/test_interface.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/bases/test_model.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/bases/test_session.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/commands/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/commands/conftest.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/commands/test_db.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/commands/test_export.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/commands/test_security.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/commands/test_security_crypto.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/commands/test_translate.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/conftest.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/test_cli.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/test_decorators.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/test_types.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/test_utils.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/file_managers/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/file_managers/conftest.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/file_managers/test_file_manager.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/file_managers/test_image_manager.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/file_managers/test_s3_file_manager.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/file_managers/test_s3_image_manager.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/lang/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/lang/test_babel_cli.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/lang/test_babel_config.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/lang/test_lazy_text.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/security/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/security/sqla/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/security/sqla/conftest.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/security/sqla/test_models.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/security/sqla/test_security_manager.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_config.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_const.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_decorators.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_exceptions.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_fastapi_react_toolkit.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_filters.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_globals.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_middlewares.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_mixins.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_models.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_registry.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_routers.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_schemas.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_types.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_version.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/__init__.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_async_task_runner.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_class_factory.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_csv_json_converter.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_deep_merge.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_extender_mixin.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_flask_appbuilder_utils.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_formatter.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_hooks.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_lazy.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_merge_schema.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_multiple_async_contexts.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_pydantic.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_run_utils.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_self_dependencies.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_smartdefaultdict.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_sqla.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_timezone.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_update_signature.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_use_default_when_none.py +0 -0
- {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: fastapi-rtk
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.12.0
|
|
4
4
|
Summary: A package that provides a set of tools to build a FastAPI application with a Class-Based CRUD API.
|
|
5
5
|
Project-URL: Homepage, https://codeberg.org/datatactics/fastapi-rtk
|
|
6
6
|
Project-URL: Issues, https://codeberg.org/datatactics/fastapi-rtk/issues
|
|
@@ -7,6 +7,7 @@ from .auth import *
|
|
|
7
7
|
from .backends.generic import *
|
|
8
8
|
from .backends.sqla import *
|
|
9
9
|
from .bases import *
|
|
10
|
+
from .caches import *
|
|
10
11
|
from .const import *
|
|
11
12
|
from .db import *
|
|
12
13
|
from .decorators import *
|
|
@@ -22,6 +23,7 @@ from .routers import *
|
|
|
22
23
|
from .schemas import *
|
|
23
24
|
from .security import *
|
|
24
25
|
from .security.sqla.apis import * #! Manually import security APIs to prevent circular imports
|
|
26
|
+
from .security.sqla.user_cache import *
|
|
25
27
|
from .setting import *
|
|
26
28
|
from .types import *
|
|
27
29
|
from .utils import *
|
|
@@ -143,6 +145,8 @@ __all__ = [
|
|
|
143
145
|
"SQLASessionMaker",
|
|
144
146
|
"SQLAScopedSession",
|
|
145
147
|
# .bases
|
|
148
|
+
"AbstractCacheSerializer",
|
|
149
|
+
"AbstractCacheStorage",
|
|
146
150
|
"DBQueryParams",
|
|
147
151
|
"AbstractQueryBuilder",
|
|
148
152
|
"BaseFileException",
|
|
@@ -157,6 +161,11 @@ __all__ = [
|
|
|
157
161
|
"MissingGreenletError",
|
|
158
162
|
"BasicModel",
|
|
159
163
|
"AbstractSession",
|
|
164
|
+
# .caches
|
|
165
|
+
"Cache",
|
|
166
|
+
"JSONCacheSerializer",
|
|
167
|
+
"PickleCacheSerializer",
|
|
168
|
+
"InMemoryCacheStorage",
|
|
160
169
|
# .const
|
|
161
170
|
"logger",
|
|
162
171
|
# .db
|
|
@@ -236,6 +245,14 @@ __all__ = [
|
|
|
236
245
|
"PermissionViewApi",
|
|
237
246
|
"UsersApi",
|
|
238
247
|
"RolesApi",
|
|
248
|
+
# .security.sqla.user_cache
|
|
249
|
+
"USER_CACHE_KEY_PREFIX",
|
|
250
|
+
"CachedUser",
|
|
251
|
+
"CachedUserDatabase",
|
|
252
|
+
"get_cached_user",
|
|
253
|
+
"set_cached_user",
|
|
254
|
+
"invalidate_user_cache",
|
|
255
|
+
"clear_user_cache",
|
|
239
256
|
# .setting
|
|
240
257
|
"Setting",
|
|
241
258
|
# .types
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.12.0"
|
|
@@ -12,6 +12,7 @@ import makefun
|
|
|
12
12
|
from ..const import DEFAULT_COOKIE_NAME, DEFAULT_SECRET, DEFAULT_TOKEN_URL, logger
|
|
13
13
|
from ..db import User, UserDatabase, get_user_db
|
|
14
14
|
from ..manager import UserManager
|
|
15
|
+
from ..security.sqla.user_cache import CachedUserDatabase
|
|
15
16
|
from .strategies import Strategy, StrategyConfig
|
|
16
17
|
from .strategies.jwt import get_jwt_strategy_generator
|
|
17
18
|
|
|
@@ -85,9 +86,10 @@ class Authenticator(fastapi_users.authentication.Authenticator):
|
|
|
85
86
|
get_enabled_backends: fastapi_users.authentication.authenticator.EnabledBackendsDependency
|
|
86
87
|
| None = None,
|
|
87
88
|
default_to_none: bool = False,
|
|
89
|
+
use_cache: bool = False,
|
|
88
90
|
):
|
|
89
91
|
"""
|
|
90
|
-
Modified version of the default `current_user` function. This adds `default_to_none`
|
|
92
|
+
Modified version of the default `current_user` function. This adds `default_to_none` and `use_cache` parameters.
|
|
91
93
|
|
|
92
94
|
Return a dependency callable to retrieve currently authenticated user.
|
|
93
95
|
|
|
@@ -108,6 +110,9 @@ class Authenticator(fastapi_users.authentication.Authenticator):
|
|
|
108
110
|
By default, all specified authentication backends are enabled.
|
|
109
111
|
Please note however that every backend will appear in the OpenAPI documentation,
|
|
110
112
|
as FastAPI resolves it statically.
|
|
113
|
+
:param use_cache: If `True`, the user is looked up through the user cache honoring `AUTH_USER_CACHE_TTL`,
|
|
114
|
+
leaving the session untouched on a hit. Only for read paths: a cached user is detached and carries no
|
|
115
|
+
fresher state than the entry. Defaults to `False`.
|
|
111
116
|
"""
|
|
112
117
|
signature = self._get_dependency_signature(get_enabled_backends)
|
|
113
118
|
|
|
@@ -120,6 +125,7 @@ class Authenticator(fastapi_users.authentication.Authenticator):
|
|
|
120
125
|
verified=verified,
|
|
121
126
|
superuser=superuser,
|
|
122
127
|
default_to_none=default_to_none,
|
|
128
|
+
use_cache=use_cache,
|
|
123
129
|
**kwargs,
|
|
124
130
|
)
|
|
125
131
|
return user
|
|
@@ -164,6 +170,7 @@ class Authenticator(fastapi_users.authentication.Authenticator):
|
|
|
164
170
|
verified: bool = False,
|
|
165
171
|
superuser: bool = False,
|
|
166
172
|
default_to_none: bool = False,
|
|
173
|
+
use_cache: bool = False,
|
|
167
174
|
strategy_params: tuple[tuple, dict[str, typing.Any]] | None = None,
|
|
168
175
|
):
|
|
169
176
|
"""
|
|
@@ -178,6 +185,7 @@ class Authenticator(fastapi_users.authentication.Authenticator):
|
|
|
178
185
|
verified (bool, optional): If `True`, the authenticated user must be verified. Defaults to False.
|
|
179
186
|
superuser (bool, optional): If `True`, the authenticated user must be a superuser. Defaults to False.
|
|
180
187
|
default_to_none (bool, optional): If `True`, return `None` if there is no authenticated user instead of raising a HTTPException. Defaults to False.
|
|
188
|
+
use_cache (bool, optional): If `True`, the user is looked up through the user cache honoring `AUTH_USER_CACHE_TTL`, leaving the session untouched on a hit. Only for read paths: a cached user is detached and carries no fresher state than the entry. Defaults to False.
|
|
181
189
|
strategy_params (tuple[tuple, dict[str, typing.Any]] | None, optional): Parameters for the authentication strategy. Defaults to None.
|
|
182
190
|
|
|
183
191
|
Returns:
|
|
@@ -191,6 +199,7 @@ class Authenticator(fastapi_users.authentication.Authenticator):
|
|
|
191
199
|
verified=verified,
|
|
192
200
|
superuser=superuser,
|
|
193
201
|
default_to_none=default_to_none,
|
|
202
|
+
use_cache=use_cache,
|
|
194
203
|
**(strategy_params[1] if strategy_params else {}),
|
|
195
204
|
)
|
|
196
205
|
|
|
@@ -205,8 +214,15 @@ class Authenticator(fastapi_users.authentication.Authenticator):
|
|
|
205
214
|
verified: bool = False,
|
|
206
215
|
superuser: bool = False,
|
|
207
216
|
default_to_none: bool = False,
|
|
217
|
+
use_cache: bool = False,
|
|
208
218
|
**kwargs,
|
|
209
219
|
) -> tuple[fastapi_users.models.UP | None, str | None]:
|
|
220
|
+
user_db = user_manager.user_db
|
|
221
|
+
if use_cache:
|
|
222
|
+
user_manager.user_db = CachedUserDatabase(
|
|
223
|
+
user_db.session, user_db.user_table, user_db.oauth_account_table
|
|
224
|
+
)
|
|
225
|
+
|
|
210
226
|
user, token = None, None
|
|
211
227
|
try:
|
|
212
228
|
user, token = await super()._authenticate(
|
|
@@ -221,6 +237,11 @@ class Authenticator(fastapi_users.authentication.Authenticator):
|
|
|
221
237
|
except fastapi.HTTPException as e:
|
|
222
238
|
if not default_to_none:
|
|
223
239
|
raise e
|
|
240
|
+
finally:
|
|
241
|
+
# The user manager is shared with the rest of the request, and only this
|
|
242
|
+
# lookup may answer from the cache: a cached user is detached, so a later
|
|
243
|
+
# write through it would clash with the session's own copy of that row.
|
|
244
|
+
user_manager.user_db = user_db
|
|
224
245
|
return user, token
|
|
225
246
|
|
|
226
247
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from .cache import *
|
|
1
2
|
from .db import *
|
|
2
3
|
from .file_manager import *
|
|
3
4
|
from .filter import *
|
|
@@ -6,6 +7,8 @@ from .model import *
|
|
|
6
7
|
from .session import *
|
|
7
8
|
|
|
8
9
|
__all__ = [
|
|
10
|
+
"AbstractCacheSerializer",
|
|
11
|
+
"AbstractCacheStorage",
|
|
9
12
|
"DBQueryParams",
|
|
10
13
|
"AbstractQueryBuilder",
|
|
11
14
|
"BaseFileException",
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
__all__ = ["AbstractCacheSerializer", "AbstractCacheStorage"]
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AbstractCacheSerializer(abc.ABC):
|
|
8
|
+
"""
|
|
9
|
+
Converts a cached value to and from the form a storage can hold.
|
|
10
|
+
|
|
11
|
+
Implement this to control the wire format of a storage that cannot hold Python objects, e.g. JSON or pickle for redis. The signature picks how `Cache` schedules the call: an `async def` is awaited on the event loop, while a plain `def` is treated as heavy and runs in a thread. Declare `async def` unless the conversion is costly enough to be worth a thread hop.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
@abc.abstractmethod
|
|
15
|
+
async def dumps(self, value: typing.Any) -> typing.Any:
|
|
16
|
+
"""
|
|
17
|
+
Convert a value into its stored form.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
value (typing.Any): The value handed to `Cache.set`.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
typing.Any: The value to hand to the storage.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
@abc.abstractmethod
|
|
27
|
+
async def loads(self, value: typing.Any) -> typing.Any:
|
|
28
|
+
"""
|
|
29
|
+
Convert a stored value back into its original form.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
value (typing.Any): The value read from the storage.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
typing.Any: The value to hand back to the caller.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class AbstractCacheStorage(abc.ABC):
|
|
40
|
+
"""
|
|
41
|
+
Keeps already-serialized values under string keys, with a per-entry expiry.
|
|
42
|
+
|
|
43
|
+
Implement this to hold entries somewhere other than the process memory, e.g. redis or memcached.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
@abc.abstractmethod
|
|
47
|
+
async def get(self, key: str) -> typing.Any | None:
|
|
48
|
+
"""
|
|
49
|
+
Read the stored value under the given key.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
key (str): The key to read.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
typing.Any | None: The stored value, or `None` when absent or expired.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
@abc.abstractmethod
|
|
59
|
+
async def set(self, key: str, value: typing.Any, ttl: int) -> None:
|
|
60
|
+
"""
|
|
61
|
+
Write a value under the given key.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
key (str): The key to write.
|
|
65
|
+
value (typing.Any): The already-serialized value to store.
|
|
66
|
+
ttl (int): Seconds the entry stays valid.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
@abc.abstractmethod
|
|
70
|
+
async def delete(self, key: str) -> None:
|
|
71
|
+
"""
|
|
72
|
+
Drop the entry under the given key.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
key (str): The key to drop.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
@abc.abstractmethod
|
|
79
|
+
async def clear(self, prefix: str | None = None) -> None:
|
|
80
|
+
"""
|
|
81
|
+
Drop every entry, or every entry whose key starts with the given prefix.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
prefix (str | None, optional): Only drop keys starting with this. Defaults to None, which drops everything.
|
|
85
|
+
"""
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from ..bases.cache import AbstractCacheSerializer, AbstractCacheStorage
|
|
4
|
+
from ..utils import smart_run
|
|
5
|
+
from .storages import InMemoryCacheStorage
|
|
6
|
+
|
|
7
|
+
__all__ = ["Cache"]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Cache:
|
|
11
|
+
"""
|
|
12
|
+
Cache pairing a storage with an optional serializer.
|
|
13
|
+
|
|
14
|
+
Swap the storage to move entries out of the process, and the serializer to match what that storage can hold.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
storage: AbstractCacheStorage | None = None,
|
|
20
|
+
serializer: AbstractCacheSerializer | None = None,
|
|
21
|
+
):
|
|
22
|
+
"""
|
|
23
|
+
Initialize the cache.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
storage (AbstractCacheStorage | None, optional): Where entries are kept. Defaults to None, which uses an `InMemoryCacheStorage`.
|
|
27
|
+
serializer (AbstractCacheSerializer | None, optional): Applied on write and reversed on read. Defaults to None, which stores values as handed in and only works for a storage holding Python objects.
|
|
28
|
+
"""
|
|
29
|
+
self.storage = storage or InMemoryCacheStorage()
|
|
30
|
+
self.serializer = serializer
|
|
31
|
+
|
|
32
|
+
async def get(self, key: str) -> typing.Any | None:
|
|
33
|
+
"""
|
|
34
|
+
Read the value cached under the given key.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
key (str): The key to read.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
typing.Any | None: The cached value, or `None` when absent or expired.
|
|
41
|
+
"""
|
|
42
|
+
value = await self.storage.get(key)
|
|
43
|
+
if value is None or self.serializer is None:
|
|
44
|
+
return value
|
|
45
|
+
|
|
46
|
+
return await smart_run(self.serializer.loads, value)
|
|
47
|
+
|
|
48
|
+
async def set(self, key: str, value: typing.Any, ttl: int) -> None:
|
|
49
|
+
"""
|
|
50
|
+
Cache a value under the given key.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
key (str): The key to write.
|
|
54
|
+
value (typing.Any): The value to cache.
|
|
55
|
+
ttl (int): Seconds the entry stays valid.
|
|
56
|
+
"""
|
|
57
|
+
if self.serializer is not None:
|
|
58
|
+
value = await smart_run(self.serializer.dumps, value)
|
|
59
|
+
|
|
60
|
+
await self.storage.set(key, value, ttl)
|
|
61
|
+
|
|
62
|
+
async def delete(self, key: str) -> None:
|
|
63
|
+
"""
|
|
64
|
+
Drop the entry cached under the given key.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
key (str): The key to drop.
|
|
68
|
+
"""
|
|
69
|
+
await self.storage.delete(key)
|
|
70
|
+
|
|
71
|
+
async def clear(self, prefix: str | None = None) -> None:
|
|
72
|
+
"""
|
|
73
|
+
Drop every cached entry, or every entry whose key starts with the given prefix.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
prefix (str | None, optional): Only drop keys starting with this. Defaults to None, which drops everything.
|
|
77
|
+
"""
|
|
78
|
+
await self.storage.clear(prefix)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import pickle
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
from ..bases.cache import AbstractCacheSerializer
|
|
6
|
+
|
|
7
|
+
__all__ = ["JSONCacheSerializer", "PickleCacheSerializer"]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class JSONCacheSerializer(AbstractCacheSerializer):
|
|
11
|
+
"""
|
|
12
|
+
Stores values as JSON text.
|
|
13
|
+
|
|
14
|
+
Values must be JSON-native; a payload holding `datetime` or `Decimal` needs `PickleCacheSerializer` or a custom encoder.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
dumps_kwargs: dict[str, typing.Any] | None = None,
|
|
20
|
+
loads_kwargs: dict[str, typing.Any] | None = None,
|
|
21
|
+
):
|
|
22
|
+
"""
|
|
23
|
+
Initialize the serializer.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
dumps_kwargs (dict[str, typing.Any] | None, optional): Keyword arguments passed to `json.dumps`, e.g. a custom `cls` encoder. Defaults to None.
|
|
27
|
+
loads_kwargs (dict[str, typing.Any] | None, optional): Keyword arguments passed to `json.loads`. Defaults to None.
|
|
28
|
+
"""
|
|
29
|
+
self.dumps_kwargs = dumps_kwargs or {}
|
|
30
|
+
self.loads_kwargs = loads_kwargs or {}
|
|
31
|
+
|
|
32
|
+
async def dumps(self, value: typing.Any) -> str:
|
|
33
|
+
"""
|
|
34
|
+
Convert a value into JSON text.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
value (typing.Any): The value to store.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
str: The JSON text to hand to the storage.
|
|
41
|
+
"""
|
|
42
|
+
return json.dumps(value, **self.dumps_kwargs)
|
|
43
|
+
|
|
44
|
+
async def loads(self, value: str) -> typing.Any:
|
|
45
|
+
"""
|
|
46
|
+
Convert JSON text back into a value.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
value (str): The JSON text read from the storage.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
typing.Any: The decoded value.
|
|
53
|
+
"""
|
|
54
|
+
return json.loads(value, **self.loads_kwargs)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class PickleCacheSerializer(AbstractCacheSerializer):
|
|
58
|
+
"""
|
|
59
|
+
Stores values as pickled bytes, keeping types JSON cannot express.
|
|
60
|
+
|
|
61
|
+
Only point this at a store the application itself owns: unpickling attacker-controlled bytes executes arbitrary code.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
def __init__(self, protocol: int = pickle.HIGHEST_PROTOCOL):
|
|
65
|
+
"""
|
|
66
|
+
Initialize the serializer.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
protocol (int, optional): The pickle protocol to write with. Defaults to `pickle.HIGHEST_PROTOCOL`.
|
|
70
|
+
"""
|
|
71
|
+
self.protocol = protocol
|
|
72
|
+
|
|
73
|
+
async def dumps(self, value: typing.Any) -> bytes:
|
|
74
|
+
"""
|
|
75
|
+
Convert a value into pickled bytes.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
value (typing.Any): The value to store.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
bytes: The pickled value to hand to the storage.
|
|
82
|
+
"""
|
|
83
|
+
return pickle.dumps(value, protocol=self.protocol)
|
|
84
|
+
|
|
85
|
+
async def loads(self, value: bytes) -> typing.Any:
|
|
86
|
+
"""
|
|
87
|
+
Convert pickled bytes back into a value.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
value (bytes): The pickled value read from the storage.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
typing.Any: The unpickled value.
|
|
94
|
+
"""
|
|
95
|
+
return pickle.loads(value)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
from ..bases.cache import AbstractCacheStorage
|
|
5
|
+
|
|
6
|
+
__all__ = ["InMemoryCacheStorage"]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class InMemoryCacheStorage(AbstractCacheStorage):
|
|
10
|
+
"""
|
|
11
|
+
Storage backed by a dictionary in the worker process.
|
|
12
|
+
|
|
13
|
+
Each worker keeps its own entries, so a deletion only reaches the worker that performed it and the others fall back to the TTL.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
"""
|
|
18
|
+
Initialize the storage with no entries.
|
|
19
|
+
"""
|
|
20
|
+
self._entries: dict[str, tuple[float, typing.Any]] = {}
|
|
21
|
+
|
|
22
|
+
async def get(self, key: str) -> typing.Any | None:
|
|
23
|
+
"""
|
|
24
|
+
Read the stored value under the given key, dropping it when it expired.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
key (str): The key to read.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
typing.Any | None: The stored value, or `None` when absent or expired.
|
|
31
|
+
"""
|
|
32
|
+
entry = self._entries.get(key)
|
|
33
|
+
if entry is None:
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
expires_at, value = entry
|
|
37
|
+
if expires_at <= time.monotonic():
|
|
38
|
+
self._entries.pop(key, None)
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
return value
|
|
42
|
+
|
|
43
|
+
async def set(self, key: str, value: typing.Any, ttl: int) -> None:
|
|
44
|
+
"""
|
|
45
|
+
Write a value under the given key, dropping entries that expired meanwhile.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
key (str): The key to write.
|
|
49
|
+
value (typing.Any): The already-serialized value to store.
|
|
50
|
+
ttl (int): Seconds the entry stays valid.
|
|
51
|
+
"""
|
|
52
|
+
now = time.monotonic()
|
|
53
|
+
for expired in [k for k, (exp, _) in self._entries.items() if exp <= now]:
|
|
54
|
+
self._entries.pop(expired, None)
|
|
55
|
+
self._entries[key] = (now + ttl, value)
|
|
56
|
+
|
|
57
|
+
async def delete(self, key: str) -> None:
|
|
58
|
+
"""
|
|
59
|
+
Drop the entry under the given key.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
key (str): The key to drop.
|
|
63
|
+
"""
|
|
64
|
+
self._entries.pop(key, None)
|
|
65
|
+
|
|
66
|
+
async def clear(self, prefix: str | None = None) -> None:
|
|
67
|
+
"""
|
|
68
|
+
Drop every entry, or every entry whose key starts with the given prefix.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
prefix (str | None, optional): Only drop keys starting with this. Defaults to None, which drops everything.
|
|
72
|
+
"""
|
|
73
|
+
if prefix is None:
|
|
74
|
+
self._entries.clear()
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
for key in [k for k in self._entries if k.startswith(prefix)]:
|
|
78
|
+
self._entries.pop(key, None)
|
|
@@ -71,7 +71,7 @@ class UserDatabase(
|
|
|
71
71
|
statement = sqlalchemy.select(self.user_table).where(
|
|
72
72
|
sqlalchemy.func.lower(self.user_table.email) == sqlalchemy.func.lower(email)
|
|
73
73
|
)
|
|
74
|
-
return await self.
|
|
74
|
+
return await self._get_first_user(statement, field="email")
|
|
75
75
|
|
|
76
76
|
async def get_by_oauth_account(
|
|
77
77
|
self, oauth: str, account_id: str
|
|
@@ -149,7 +149,7 @@ class UserDatabase(
|
|
|
149
149
|
sqlalchemy.func.lower(self.user_table.username)
|
|
150
150
|
== sqlalchemy.func.lower(username)
|
|
151
151
|
)
|
|
152
|
-
return await self.
|
|
152
|
+
return await self._get_first_user(statement, field="username")
|
|
153
153
|
|
|
154
154
|
async def _get_user(
|
|
155
155
|
self, statement: sqlalchemy.Select
|
|
@@ -157,6 +157,30 @@ class UserDatabase(
|
|
|
157
157
|
results = await smart_run(self.session.execute, statement)
|
|
158
158
|
return results.unique().scalar_one_or_none()
|
|
159
159
|
|
|
160
|
+
async def _get_first_user(
|
|
161
|
+
self, statement: sqlalchemy.Select, *, field: str
|
|
162
|
+
) -> fastapi_users.models.UP | None:
|
|
163
|
+
"""
|
|
164
|
+
Resolve a case-insensitive user lookup deterministically.
|
|
165
|
+
|
|
166
|
+
Legacy databases may contain rows that differ only in the casing of
|
|
167
|
+
`email`/`username` (the unique constraint is case-sensitive). Instead
|
|
168
|
+
of raising `MultipleResultsFound` (which surfaced as HTTP 500 on
|
|
169
|
+
login), the lowest-id matching row wins and a warning is logged.
|
|
170
|
+
"""
|
|
171
|
+
statement = statement.order_by(self.user_table.id)
|
|
172
|
+
results = await smart_run(self.session.execute, statement)
|
|
173
|
+
users = results.unique().scalars().all()
|
|
174
|
+
if len(users) > 1:
|
|
175
|
+
logger.warning(
|
|
176
|
+
"Multiple users match %s case-insensitively (ids=%s). "
|
|
177
|
+
"Using the lowest-id row (id=%s); please de-duplicate the user table.",
|
|
178
|
+
field,
|
|
179
|
+
[user.id for user in users],
|
|
180
|
+
users[0].id,
|
|
181
|
+
)
|
|
182
|
+
return users[0] if users else None
|
|
183
|
+
|
|
160
184
|
|
|
161
185
|
class lazy(lazy_self["DatabaseSessionManager", T]): ...
|
|
162
186
|
|
|
@@ -19,6 +19,8 @@ def set_global_user():
|
|
|
19
19
|
"""
|
|
20
20
|
A dependency for FastAPI that will set the current user to the global variable `g.user`.
|
|
21
21
|
|
|
22
|
+
The user is read through `g.cache` when `AUTH_USER_CACHE_TTL` is set, so a cached request issues no query and never checks out a database connection for the lookup.
|
|
23
|
+
|
|
22
24
|
Usage:
|
|
23
25
|
```python
|
|
24
26
|
async def get_info(
|
|
@@ -31,7 +33,9 @@ def set_global_user():
|
|
|
31
33
|
|
|
32
34
|
async def set_global_user_dependency(
|
|
33
35
|
user: User | None = fastapi.Depends(
|
|
34
|
-
g.auth.fastapi_users.current_user(
|
|
36
|
+
g.auth.fastapi_users.current_user(
|
|
37
|
+
active=True, default_to_none=True, use_cache=True
|
|
38
|
+
)
|
|
35
39
|
),
|
|
36
40
|
):
|
|
37
41
|
g.user = user
|
|
@@ -20,6 +20,7 @@ from .api import BaseApi, ModelRestApi
|
|
|
20
20
|
from .apis import InfoApi, LicenseApi
|
|
21
21
|
from .auth import Auth
|
|
22
22
|
from .backends.sqla.session import SQLASession
|
|
23
|
+
from .caches import Cache
|
|
23
24
|
from .cli.commands.db import upgrade
|
|
24
25
|
from .cli.commands.translate import init_babel_cli
|
|
25
26
|
from .const import (
|
|
@@ -90,6 +91,7 @@ class FastAPIReactToolkit:
|
|
|
90
91
|
exclude_apis (list[BASE_APIS] | None, optional): List of security APIs to be excluded when initializing the FastAPI application. Defaults to None.
|
|
91
92
|
global_user_dependency (bool, optional): Whether to add the `set_global_user` dependency to the FastAPI application. This allows you to access the current user with the `g.user` object. Defaults to True.
|
|
92
93
|
global_background_tasks (bool, optional): Whether to add the `set_global_background_tasks` dependency to the FastAPI application. This allows you to access the background tasks with the `g.background_tasks` object. Defaults to True.
|
|
94
|
+
cache (Cache | None, optional): The cache backend used by caching features such as `AUTH_USER_CACHE_TTL`, also reachable as `g.cache`. Defaults to None, which uses the `CACHE` config key or a `Cache` backed by an `InMemoryCacheStorage`.
|
|
93
95
|
instrumentator (Instrumentator | None, optional): The instrumentator to use for monitoring the FastAPI application. Defaults to `Instrumentator(**Setting.INSTRUMENTATOR_CONFIG)`.
|
|
94
96
|
on_startup (Callable[[FastAPI], None] | Awaitable[Callable[[FastAPI], None]] | None, optional): Function to call when the app is starting up. Can either be a regular function or an async function. If the function takes a `FastAPI` instance as an argument, it will be passed to the function. Defaults to None.
|
|
95
97
|
on_shutdown (Callable[[FastAPI], None] | Awaitable[Callable[[FastAPI], None]] | None, optional): Function to call when the app is shutting down. Can either be a regular function or an async function. If the function takes a `FastAPI` instance as an argument, it will be passed to the function. Defaults to None.
|
|
@@ -212,6 +214,7 @@ class FastAPIReactToolkit:
|
|
|
212
214
|
global_user_dependency: bool = True,
|
|
213
215
|
global_background_tasks: bool = True,
|
|
214
216
|
global_request: bool = True,
|
|
217
|
+
cache: Cache | None = None,
|
|
215
218
|
instrumentator: prometheus_fastapi_instrumentator.Instrumentator | None = None,
|
|
216
219
|
on_startup: (
|
|
217
220
|
collections.abc.Callable[[fastapi.FastAPI], None]
|
|
@@ -246,6 +249,7 @@ class FastAPIReactToolkit:
|
|
|
246
249
|
global_user_dependency (bool, optional): Whether to add the `set_global_user` dependency to the FastAPI application. This allows you to access the current user with the `g.user` object. Defaults to True.
|
|
247
250
|
global_background_tasks (bool, optional): Whether to add the `set_global_background_tasks` dependency to the FastAPI application. This allows you to access the background tasks with the `g.background_tasks` object. Defaults to True.
|
|
248
251
|
global_request (bool, optional): Whether to add the `set_global_request` dependency to the FastAPI application. This allows you to access the current request with the `g.request` object. Defaults to True.
|
|
252
|
+
cache (Cache | None, optional): The cache backend used by caching features such as `AUTH_USER_CACHE_TTL`, also reachable as `g.cache`. Defaults to None, which uses the `CACHE` config key or a `Cache` backed by an `InMemoryCacheStorage`.
|
|
249
253
|
instrumentator (Instrumentator | None, optional): The instrumentator to use for monitoring the FastAPI application. Defaults to `Instrumentator(**Setting.INSTRUMENTATOR_CONFIG)`.
|
|
250
254
|
on_startup (Callable[[FastAPI], None] | Awaitable[Callable[[FastAPI], None]] | None, optional): Function to call when the app is starting up. Can either be a regular function or an async function. If the function takes a `FastAPI` instance as an argument, it will be passed to the function. Defaults to None.
|
|
251
255
|
on_shutdown (Callable[[FastAPI], None] | Awaitable[Callable[[FastAPI], None]] | None, optional): Function to call when the app is shutting down. Can either be a regular function or an async function. If the function takes a `FastAPI` instance as an argument, it will be passed to the function. Defaults to None.
|
|
@@ -265,6 +269,8 @@ class FastAPIReactToolkit:
|
|
|
265
269
|
self.global_user_dependency = global_user_dependency
|
|
266
270
|
self.global_background_tasks = global_background_tasks
|
|
267
271
|
self.global_request = global_request
|
|
272
|
+
if cache is not None:
|
|
273
|
+
self.cache = cache
|
|
268
274
|
if instrumentator is not None:
|
|
269
275
|
self.instrumentator = instrumentator
|
|
270
276
|
self.on_startup = on_startup
|
|
@@ -798,3 +804,25 @@ class FastAPIReactToolkit:
|
|
|
798
804
|
@property
|
|
799
805
|
def config(self):
|
|
800
806
|
return g.config
|
|
807
|
+
|
|
808
|
+
@property
|
|
809
|
+
def cache(self) -> Cache:
|
|
810
|
+
"""
|
|
811
|
+
The cache used by caching features such as `AUTH_USER_CACHE_TTL`.
|
|
812
|
+
|
|
813
|
+
Reads and writes `g.cache`, so the toolkit and the rest of the framework always see the same instance.
|
|
814
|
+
|
|
815
|
+
Returns:
|
|
816
|
+
Cache: The active cache, built from the `CACHE` config key or defaulting to an `InMemoryCacheStorage`.
|
|
817
|
+
"""
|
|
818
|
+
return g.cache
|
|
819
|
+
|
|
820
|
+
@cache.setter
|
|
821
|
+
def cache(self, value: Cache) -> None:
|
|
822
|
+
"""
|
|
823
|
+
Replace the cache used by caching features.
|
|
824
|
+
|
|
825
|
+
Args:
|
|
826
|
+
value (Cache): The cache to use.
|
|
827
|
+
"""
|
|
828
|
+
g.cache = value
|