fastapi-basekit 0.5.0__tar.gz → 0.5.2__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_basekit-0.5.0 → fastapi_basekit-0.5.2}/PKG-INFO +1 -1
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/beanie/repository/base.py +13 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/controller/base.py +37 -6
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlalchemy/repository/base.py +14 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlmodel/repository/base.py +15 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit.egg-info/PKG-INFO +1 -1
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/pyproject.toml +1 -1
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_action_param_leak.py +41 -2
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_beanie_repository_regression.py +34 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/LICENSE +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/README.md +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/beanie/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/beanie/controller/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/beanie/controller/base.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/beanie/repository/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/beanie/service/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/beanie/service/base.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/controller/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/permissions/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/permissions/base.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlalchemy/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlalchemy/controller/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlalchemy/controller/base.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlalchemy/repository/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlalchemy/service/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlalchemy/service/base.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlalchemy/session.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlmodel/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlmodel/controller/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlmodel/controller/base.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlmodel/repository/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlmodel/service/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlmodel/service/base.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/cli/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/cli/main.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/exceptions/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/exceptions/api_exceptions.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/exceptions/domain.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/exceptions/handler.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/openapi.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/py.typed +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/schema/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/schema/base.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/schema/jwt.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/schema/schema.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/servicios/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/servicios/thrid/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/servicios/thrid/jwt.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/cookiecutter.json +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/hooks/post_gen_project.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/hooks/pre_gen_project.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/.env.example +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/.gitignore +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/Dockerfile +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/LICENSE +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/Makefile +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/README.md +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/alembic/env.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/alembic/script.py.mako +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/alembic.ini +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/endpoints/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/endpoints/auth/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/endpoints/auth/auth.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/endpoints/user/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/endpoints/user/user.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/routers.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/config/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/config/database.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/config/settings.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/main.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/middleware/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/middleware/auth.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/middleware/permissions.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/models/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/models/auth.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/models/base.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/models/enums.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/models/types.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/permissions/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/permissions/user.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/repositories/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/repositories/user/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/repositories/user/repository.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/schemas/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/schemas/auth.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/schemas/base.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/schemas/user.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/scripts/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/scripts/init.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/scripts/init_admin.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/services/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/services/auth_service.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/services/dependency.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/services/user_service.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/utils/__init__.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/utils/exception_handlers.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/utils/security.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/docker-compose.yml +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/pytest.ini +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/requirements.txt +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit.egg-info/SOURCES.txt +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit.egg-info/dependency_links.txt +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit.egg-info/entry_points.txt +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit.egg-info/requires.txt +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit.egg-info/top_level.txt +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/setup.cfg +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_api_exceptions.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_base_response.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_base_service.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_beanie_aggregation_hooks.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_beanie_aggregation_integration.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_beanie_keyset_pagination.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_beanie_service_mutable_defaults.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_beanie_service_regression.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_controller_auto_permissions.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_crud_beanie_controller.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_crud_controller.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_crud_sqlmodel_repository_service.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_exception_handlers.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_filter_operators.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_generics_optional_and_cache.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_generics_wiring.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_jwt_config_robustness.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_jwt_service.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_openapi_simplify.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_params_classification.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_params_coercion_matrix.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_params_coercion_unit.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_params_http_matrix_beanie.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_params_http_matrix_sqlalchemy.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_params_rich_type_coercion.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_path_filter_mixin_compat.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_post_process_list_hook.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_pulbot_cbv_pattern_beanie.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_repo_signature_aliases.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_sql_queryset_override.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_sqlalchemy_base_service_order.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_sqlalchemy_repository_regression.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_sqlalchemy_service_regression.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_sqlmodel_filter_operators.py +0 -0
- {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_typed_filter_coercion.py +0 -0
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/beanie/repository/base.py
RENAMED
|
@@ -215,6 +215,19 @@ class BaseRepository(Generic[ModelT]):
|
|
|
215
215
|
exprs.append(field_attr.id == _coerce_objectid(v))
|
|
216
216
|
continue
|
|
217
217
|
|
|
218
|
+
# Clave no resoluble (typo, campo inexistente, nesting mal escrito).
|
|
219
|
+
# Se descarta — pero se AVISA: si la clave venía de `get_filters`
|
|
220
|
+
# (scoping por tenant/owner), su desaparición silenciosa deja el
|
|
221
|
+
# listado sin filtrar → fuga cross-tenant (IDOR). El warning hace
|
|
222
|
+
# visible la pérdida en logs/Sentry en vez de fallar en silencio.
|
|
223
|
+
logger.warning(
|
|
224
|
+
"build_filter_query: filtro '%s' descartado (no resuelve a "
|
|
225
|
+
"ningún campo de %s). Si era un filtro de scoping, el listado "
|
|
226
|
+
"queda SIN ese filtro.",
|
|
227
|
+
k,
|
|
228
|
+
getattr(self.model, "__name__", self.model),
|
|
229
|
+
)
|
|
230
|
+
|
|
218
231
|
# Raw MongoDB filters go first so Beanie processes them as a dict condition
|
|
219
232
|
query_args: list = ([raw_filters] if raw_filters else []) + exprs
|
|
220
233
|
query = self.model.find(*query_args, **self._get_query_kwargs(**kwargs))
|
|
@@ -79,12 +79,41 @@ class BaseController:
|
|
|
79
79
|
# DRF Style: Permisos globales por defecto
|
|
80
80
|
permission_classes: ClassVar[List[Type[BasePermission]]] = []
|
|
81
81
|
|
|
82
|
-
# ClassVar so cbv (fastapi-restful) does NOT promote it to a query param.
|
|
83
|
-
# A plain `Optional[str]` annotation here leaks a spurious `action` query
|
|
84
|
-
# parameter onto every mounted endpoint. `prepare_action` still assigns
|
|
85
|
-
# `self.action` (instance attr) at runtime, which works fine.
|
|
86
|
-
action: ClassVar[Optional[str]] = None
|
|
87
82
|
request: Request
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def action(self) -> Optional[str]:
|
|
86
|
+
"""Acción canónica del request actual.
|
|
87
|
+
|
|
88
|
+
Preferencia: la que fijó ``prepare_action`` (``list``/``retrieve``/…).
|
|
89
|
+
Fallback: el nombre de la función de endpoint que FastAPI está
|
|
90
|
+
ejecutando (``create``/``update``/``retrieve``/…), leído de
|
|
91
|
+
``request.scope["endpoint"]``. Así un endpoint CUSTOM que llama
|
|
92
|
+
``format_response`` sin ``prepare_action`` igual obtiene su acción para
|
|
93
|
+
``get_schema_class``/``get_filters`` — antes caía a ``None`` y devolvía
|
|
94
|
+
el schema por defecto (de lista), rompiendo la validación del
|
|
95
|
+
``response_model`` de detalle.
|
|
96
|
+
|
|
97
|
+
Es una ``property`` (no un campo anotado) a propósito: cbv
|
|
98
|
+
(fastapi-restful) NO la promueve a query param, así que no reaparece el
|
|
99
|
+
``action`` espurio en la firma de cada endpoint.
|
|
100
|
+
|
|
101
|
+
OJO: NO reemplaza a ``prepare_action`` para permisos. El fallback solo
|
|
102
|
+
informa el schema/scoping; ``check_permissions`` sigue corriendo solo si
|
|
103
|
+
el endpoint llamó ``prepare_action`` (o delega en un CRUD base)."""
|
|
104
|
+
prepared = getattr(self, "_basekit_prepared_action", None)
|
|
105
|
+
if prepared is not None:
|
|
106
|
+
return prepared
|
|
107
|
+
request = getattr(self, "request", None)
|
|
108
|
+
scope = getattr(request, "scope", None) if request is not None else None
|
|
109
|
+
endpoint = scope.get("endpoint") if isinstance(scope, dict) else None
|
|
110
|
+
return getattr(endpoint, "__name__", None)
|
|
111
|
+
|
|
112
|
+
@action.setter
|
|
113
|
+
def action(self, value: Optional[str]) -> None:
|
|
114
|
+
"""Permite fijar la acción a mano (``self.action = "list"``) — compat
|
|
115
|
+
con código pre-property. Escribe el backing attr que lee el getter."""
|
|
116
|
+
self._basekit_prepared_action = value
|
|
88
117
|
_params_excluded_fields: ClassVar[Set[str]] = {
|
|
89
118
|
"self",
|
|
90
119
|
"page",
|
|
@@ -135,7 +164,9 @@ class BaseController:
|
|
|
135
164
|
"""
|
|
136
165
|
if getattr(self, "_basekit_prepared_action", None) == action_name:
|
|
137
166
|
return
|
|
138
|
-
|
|
167
|
+
# `action` es una property de solo-lectura que expone
|
|
168
|
+
# `_basekit_prepared_action` (con fallback al nombre del endpoint), así
|
|
169
|
+
# que fijamos el backing attr, no `self.action` directamente.
|
|
139
170
|
self._basekit_prepared_action = action_name
|
|
140
171
|
# Propagate the canonical CRUD action ("list"/"retrieve"/...) to the
|
|
141
172
|
# service so `get_kwargs_query` / `get_filters` can branch on it. The
|
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlalchemy/repository/base.py
RENAMED
|
@@ -11,6 +11,7 @@ from typing import (
|
|
|
11
11
|
Union,
|
|
12
12
|
)
|
|
13
13
|
from uuid import UUID
|
|
14
|
+
import logging
|
|
14
15
|
|
|
15
16
|
from sqlalchemy import Select, and_, or_, select, func
|
|
16
17
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
@@ -18,6 +19,8 @@ from sqlalchemy.orm import Relationship, joinedload, selectinload
|
|
|
18
19
|
|
|
19
20
|
from ....exceptions.api_exceptions import NotFoundException
|
|
20
21
|
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
21
24
|
#: Tipo del modelo ORM que gestiona el repositorio. Parametrízalo al declarar
|
|
22
25
|
#: la subclase — ``class UserRepository(BaseRepository[User])`` — para que
|
|
23
26
|
#: ``get``/``create``/``update``/``list_paginated`` devuelvan ``User`` tipado
|
|
@@ -165,6 +168,17 @@ class BaseRepository(Generic[ModelT]):
|
|
|
165
168
|
attr, field_joins = self._resolve_field_path(resolve_path)
|
|
166
169
|
|
|
167
170
|
if attr is None:
|
|
171
|
+
# Filtro no resoluble (typo, columna/relación inexistente). Se
|
|
172
|
+
# descarta — pero se AVISA: si venía de `get_filters` (scoping
|
|
173
|
+
# por tenant/owner), su desaparición silenciosa deja el listado
|
|
174
|
+
# sin filtrar → fuga cross-tenant (IDOR). El warning hace
|
|
175
|
+
# visible la pérdida en logs en vez de fallar en silencio.
|
|
176
|
+
logger.warning(
|
|
177
|
+
"filtro '%s' descartado (no resuelve a ningún campo de %s). "
|
|
178
|
+
"Si era un filtro de scoping, el listado queda SIN ese filtro.",
|
|
179
|
+
filter_path,
|
|
180
|
+
getattr(self.model, "__name__", self.model),
|
|
181
|
+
)
|
|
168
182
|
continue
|
|
169
183
|
|
|
170
184
|
joins_to_apply.update(field_joins)
|
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlmodel/repository/base.py
RENAMED
|
@@ -12,6 +12,8 @@ from typing import (
|
|
|
12
12
|
)
|
|
13
13
|
from uuid import UUID
|
|
14
14
|
|
|
15
|
+
import logging
|
|
16
|
+
|
|
15
17
|
from sqlmodel import select
|
|
16
18
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
|
17
19
|
from sqlalchemy import and_, or_, func, Select
|
|
@@ -19,6 +21,8 @@ from sqlalchemy.orm import Relationship, joinedload, selectinload
|
|
|
19
21
|
|
|
20
22
|
from ....exceptions.api_exceptions import NotFoundException
|
|
21
23
|
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
22
26
|
#: Tipo del modelo SQLModel que gestiona el repositorio. Parametrízalo al
|
|
23
27
|
#: declarar la subclase — ``class UserRepository(BaseRepository[User])``.
|
|
24
28
|
ModelT = TypeVar("ModelT")
|
|
@@ -140,6 +144,17 @@ class BaseRepository(Generic[ModelT]):
|
|
|
140
144
|
attr, field_joins = self._resolve_field_path(resolve_path)
|
|
141
145
|
|
|
142
146
|
if attr is None:
|
|
147
|
+
# Filtro no resoluble (typo, columna/relación inexistente). Se
|
|
148
|
+
# descarta — pero se AVISA: si venía de `get_filters` (scoping
|
|
149
|
+
# por tenant/owner), su desaparición silenciosa deja el listado
|
|
150
|
+
# sin filtrar → fuga cross-tenant (IDOR). El warning hace
|
|
151
|
+
# visible la pérdida en logs en vez de fallar en silencio.
|
|
152
|
+
logger.warning(
|
|
153
|
+
"filtro '%s' descartado (no resuelve a ningún campo de %s). "
|
|
154
|
+
"Si era un filtro de scoping, el listado queda SIN ese filtro.",
|
|
155
|
+
filter_path,
|
|
156
|
+
getattr(self.model, "__name__", self.model),
|
|
157
|
+
)
|
|
143
158
|
continue
|
|
144
159
|
|
|
145
160
|
joins_to_apply.update(field_joins)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "fastapi-basekit"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.2"
|
|
8
8
|
description = "Utilities and base classes for FastAPI async projects (Beanie, SQLAlchemy or SQLModel)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Regresión: cbv NO debe exponer `action` como query param.
|
|
2
2
|
|
|
3
|
-
`BaseController.action`
|
|
4
|
-
fastapi-restful (cbv) no lo promueva a parámetro de query en cada ruta
|
|
3
|
+
`BaseController.action` es una `@property` (no un campo anotado) precisamente
|
|
4
|
+
para que fastapi-restful (cbv) no lo promueva a parámetro de query en cada ruta
|
|
5
5
|
montada. Antes de 0.4.0 aparecía un `?action=` espurio en toda mutación.
|
|
6
6
|
"""
|
|
7
7
|
|
|
@@ -49,3 +49,42 @@ def test_action_is_filterable_not_excluded():
|
|
|
49
49
|
|
|
50
50
|
assert "action" not in SQLAlchemyBaseController._params_excluded_fields
|
|
51
51
|
assert "action" not in BaseController._params_excluded_fields
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_action_falls_back_to_endpoint_name():
|
|
55
|
+
"""`action` resuelve al nombre de la función de endpoint cuando el endpoint
|
|
56
|
+
custom NO llamó `prepare_action`.
|
|
57
|
+
|
|
58
|
+
Antes `action` era un ClassVar que quedaba en None → `get_schema_class`
|
|
59
|
+
devolvía el schema por defecto (de lista) y la validación del `response_model`
|
|
60
|
+
de detalle fallaba. Ahora es una property con fallback a
|
|
61
|
+
`request.scope["endpoint"].__name__`. Sigue sin filtrarse como query param
|
|
62
|
+
(test de arriba) porque una property no es un campo anotado.
|
|
63
|
+
"""
|
|
64
|
+
from fastapi_basekit.aio.controller.base import BaseController
|
|
65
|
+
|
|
66
|
+
class C(BaseController):
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
# Sin request ni prepare_action → None (no rompe).
|
|
70
|
+
assert C().action is None
|
|
71
|
+
|
|
72
|
+
# `prepare_action` (vía backing attr) gana sobre el fallback.
|
|
73
|
+
c = C()
|
|
74
|
+
c._basekit_prepared_action = "list"
|
|
75
|
+
assert c.action == "list"
|
|
76
|
+
|
|
77
|
+
# Fallback: lee el nombre de la función de endpoint activa.
|
|
78
|
+
def create(): # nombre = acción canónica
|
|
79
|
+
...
|
|
80
|
+
|
|
81
|
+
class _Req:
|
|
82
|
+
scope = {"endpoint": create}
|
|
83
|
+
|
|
84
|
+
c2 = C()
|
|
85
|
+
c2.request = _Req()
|
|
86
|
+
assert c2.action == "create"
|
|
87
|
+
|
|
88
|
+
# Setter de compat: `self.action = "x"` sigue funcionando.
|
|
89
|
+
c2.action = "update"
|
|
90
|
+
assert c2.action == "update"
|
|
@@ -163,6 +163,40 @@ async def test_filter_scalar_field(data):
|
|
|
163
163
|
assert {e.name for e in rows} == {"Ana", "Ciro"}
|
|
164
164
|
|
|
165
165
|
|
|
166
|
+
@pytest.mark.asyncio
|
|
167
|
+
async def test_mongo_style_scoping_keys_pass_through(data):
|
|
168
|
+
"""`user.$id` / `$or` (patrón de scoping en get_filters) NO se descartan:
|
|
169
|
+
se aplican como filtro Mongo crudo. Su pérdida silenciosa sería un IDOR."""
|
|
170
|
+
repo = EmployeeRepo()
|
|
171
|
+
query = repo.build_filter_query(
|
|
172
|
+
search=None,
|
|
173
|
+
search_fields=[],
|
|
174
|
+
filters={"company.$id": data["acme"].id},
|
|
175
|
+
)
|
|
176
|
+
assert query.get_filter_query() == {"company.$id": data["acme"].id}
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@pytest.mark.asyncio
|
|
180
|
+
async def test_unresolvable_filter_key_warns_before_drop(data, caplog):
|
|
181
|
+
"""Una clave que no resuelve a ningún campo se descarta PERO avisa: si era
|
|
182
|
+
un filtro de scoping, su desaparición silenciosa dejaría el listado sin
|
|
183
|
+
filtrar (fuga cross-tenant). El warning hace visible la pérdida."""
|
|
184
|
+
import logging
|
|
185
|
+
|
|
186
|
+
repo = EmployeeRepo()
|
|
187
|
+
with caplog.at_level(logging.WARNING):
|
|
188
|
+
query = repo.build_filter_query(
|
|
189
|
+
search=None,
|
|
190
|
+
search_fields=[],
|
|
191
|
+
filters={"nonexistent_scope": "x"},
|
|
192
|
+
)
|
|
193
|
+
assert query.get_filter_query() in ({}, {"$and": []})
|
|
194
|
+
assert any(
|
|
195
|
+
"nonexistent_scope" in r.message and "descartado" in r.message
|
|
196
|
+
for r in caplog.records
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
166
200
|
# ---------------------------------------------------------------------------
|
|
167
201
|
# get_by_field / get_by_fields
|
|
168
202
|
# ---------------------------------------------------------------------------
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/beanie/controller/__init__.py
RENAMED
|
File without changes
|
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/beanie/controller/base.py
RENAMED
|
File without changes
|
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/beanie/repository/__init__.py
RENAMED
|
File without changes
|
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/beanie/service/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlalchemy/controller/base.py
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlalchemy/service/__init__.py
RENAMED
|
File without changes
|
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlalchemy/service/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlmodel/controller/__init__.py
RENAMED
|
File without changes
|
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlmodel/controller/base.py
RENAMED
|
File without changes
|
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlmodel/repository/__init__.py
RENAMED
|
File without changes
|
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlmodel/service/__init__.py
RENAMED
|
File without changes
|
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlmodel/service/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/exceptions/api_exceptions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/cookiecutter.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_beanie_aggregation_integration.py
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_beanie_service_mutable_defaults.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_crud_sqlmodel_repository_service.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_sqlalchemy_repository_regression.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|