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.
Files changed (146) hide show
  1. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/PKG-INFO +1 -1
  2. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/beanie/repository/base.py +13 -0
  3. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/controller/base.py +37 -6
  4. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlalchemy/repository/base.py +14 -0
  5. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlmodel/repository/base.py +15 -0
  6. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit.egg-info/PKG-INFO +1 -1
  7. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/pyproject.toml +1 -1
  8. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_action_param_leak.py +41 -2
  9. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_beanie_repository_regression.py +34 -0
  10. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/LICENSE +0 -0
  11. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/README.md +0 -0
  12. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/__init__.py +0 -0
  13. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/__init__.py +0 -0
  14. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/beanie/__init__.py +0 -0
  15. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/beanie/controller/__init__.py +0 -0
  16. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/beanie/controller/base.py +0 -0
  17. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/beanie/repository/__init__.py +0 -0
  18. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/beanie/service/__init__.py +0 -0
  19. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/beanie/service/base.py +0 -0
  20. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/controller/__init__.py +0 -0
  21. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/permissions/__init__.py +0 -0
  22. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/permissions/base.py +0 -0
  23. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlalchemy/__init__.py +0 -0
  24. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlalchemy/controller/__init__.py +0 -0
  25. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlalchemy/controller/base.py +0 -0
  26. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlalchemy/repository/__init__.py +0 -0
  27. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlalchemy/service/__init__.py +0 -0
  28. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlalchemy/service/base.py +0 -0
  29. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlalchemy/session.py +0 -0
  30. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlmodel/__init__.py +0 -0
  31. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlmodel/controller/__init__.py +0 -0
  32. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlmodel/controller/base.py +0 -0
  33. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlmodel/repository/__init__.py +0 -0
  34. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlmodel/service/__init__.py +0 -0
  35. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/aio/sqlmodel/service/base.py +0 -0
  36. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/cli/__init__.py +0 -0
  37. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/cli/main.py +0 -0
  38. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/exceptions/__init__.py +0 -0
  39. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/exceptions/api_exceptions.py +0 -0
  40. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/exceptions/domain.py +0 -0
  41. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/exceptions/handler.py +0 -0
  42. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/openapi.py +0 -0
  43. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/py.typed +0 -0
  44. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/schema/__init__.py +0 -0
  45. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/schema/base.py +0 -0
  46. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/schema/jwt.py +0 -0
  47. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/schema/schema.py +0 -0
  48. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/servicios/__init__.py +0 -0
  49. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/servicios/thrid/__init__.py +0 -0
  50. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/servicios/thrid/jwt.py +0 -0
  51. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/cookiecutter.json +0 -0
  52. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/hooks/post_gen_project.py +0 -0
  53. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/hooks/pre_gen_project.py +0 -0
  54. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/.env.example +0 -0
  55. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/.gitignore +0 -0
  56. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/Dockerfile +0 -0
  57. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/LICENSE +0 -0
  58. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/Makefile +0 -0
  59. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/README.md +0 -0
  60. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/alembic/env.py +0 -0
  61. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/alembic/script.py.mako +0 -0
  62. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/alembic.ini +0 -0
  63. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/__init__.py +0 -0
  64. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/__init__.py +0 -0
  65. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/__init__.py +0 -0
  66. {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
  67. {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
  68. {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
  69. {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
  70. {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
  71. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/routers.py +0 -0
  72. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/config/__init__.py +0 -0
  73. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/config/database.py +0 -0
  74. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/config/settings.py +0 -0
  75. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/main.py +0 -0
  76. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/middleware/__init__.py +0 -0
  77. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/middleware/auth.py +0 -0
  78. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/middleware/permissions.py +0 -0
  79. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/models/__init__.py +0 -0
  80. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/models/auth.py +0 -0
  81. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/models/base.py +0 -0
  82. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/models/enums.py +0 -0
  83. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/models/types.py +0 -0
  84. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/permissions/__init__.py +0 -0
  85. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/permissions/user.py +0 -0
  86. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/repositories/__init__.py +0 -0
  87. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/repositories/user/__init__.py +0 -0
  88. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/repositories/user/repository.py +0 -0
  89. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/schemas/__init__.py +0 -0
  90. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/schemas/auth.py +0 -0
  91. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/schemas/base.py +0 -0
  92. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/schemas/user.py +0 -0
  93. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/scripts/__init__.py +0 -0
  94. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/scripts/init.py +0 -0
  95. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/scripts/init_admin.py +0 -0
  96. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/services/__init__.py +0 -0
  97. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/services/auth_service.py +0 -0
  98. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/services/dependency.py +0 -0
  99. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/services/user_service.py +0 -0
  100. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/utils/__init__.py +0 -0
  101. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/utils/exception_handlers.py +0 -0
  102. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/utils/security.py +0 -0
  103. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/docker-compose.yml +0 -0
  104. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/pytest.ini +0 -0
  105. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/requirements.txt +0 -0
  106. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit.egg-info/SOURCES.txt +0 -0
  107. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit.egg-info/dependency_links.txt +0 -0
  108. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit.egg-info/entry_points.txt +0 -0
  109. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit.egg-info/requires.txt +0 -0
  110. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/fastapi_basekit.egg-info/top_level.txt +0 -0
  111. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/setup.cfg +0 -0
  112. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_api_exceptions.py +0 -0
  113. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_base_response.py +0 -0
  114. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_base_service.py +0 -0
  115. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_beanie_aggregation_hooks.py +0 -0
  116. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_beanie_aggregation_integration.py +0 -0
  117. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_beanie_keyset_pagination.py +0 -0
  118. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_beanie_service_mutable_defaults.py +0 -0
  119. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_beanie_service_regression.py +0 -0
  120. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_controller_auto_permissions.py +0 -0
  121. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_crud_beanie_controller.py +0 -0
  122. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_crud_controller.py +0 -0
  123. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_crud_sqlmodel_repository_service.py +0 -0
  124. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_exception_handlers.py +0 -0
  125. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_filter_operators.py +0 -0
  126. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_generics_optional_and_cache.py +0 -0
  127. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_generics_wiring.py +0 -0
  128. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_jwt_config_robustness.py +0 -0
  129. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_jwt_service.py +0 -0
  130. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_openapi_simplify.py +0 -0
  131. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_params_classification.py +0 -0
  132. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_params_coercion_matrix.py +0 -0
  133. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_params_coercion_unit.py +0 -0
  134. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_params_http_matrix_beanie.py +0 -0
  135. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_params_http_matrix_sqlalchemy.py +0 -0
  136. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_params_rich_type_coercion.py +0 -0
  137. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_path_filter_mixin_compat.py +0 -0
  138. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_post_process_list_hook.py +0 -0
  139. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_pulbot_cbv_pattern_beanie.py +0 -0
  140. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_repo_signature_aliases.py +0 -0
  141. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_sql_queryset_override.py +0 -0
  142. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_sqlalchemy_base_service_order.py +0 -0
  143. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_sqlalchemy_repository_regression.py +0 -0
  144. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_sqlalchemy_service_regression.py +0 -0
  145. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_sqlmodel_filter_operators.py +0 -0
  146. {fastapi_basekit-0.5.0 → fastapi_basekit-0.5.2}/tests/test_typed_filter_coercion.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fastapi-basekit
3
- Version: 0.5.0
3
+ Version: 0.5.2
4
4
  Summary: Utilities and base classes for FastAPI async projects (Beanie, SQLAlchemy or SQLModel)
5
5
  Author-email: Jerson Moreno <jerson.ml820@hotmail.com>
6
6
  License: MIT
@@ -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
- self.action = action_name
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
@@ -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)
@@ -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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fastapi-basekit
3
- Version: 0.5.0
3
+ Version: 0.5.2
4
4
  Summary: Utilities and base classes for FastAPI async projects (Beanie, SQLAlchemy or SQLModel)
5
5
  Author-email: Jerson Moreno <jerson.ml820@hotmail.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "fastapi-basekit"
7
- version = "0.5.0"
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` se declara `ClassVar` precisamente para que
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