zrb 1.0.0a21__py3-none-any.whl → 1.0.0b2__py3-none-any.whl

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 (191) hide show
  1. zrb/__init__.py +2 -1
  2. zrb/__main__.py +0 -3
  3. zrb/builtin/__init__.py +3 -0
  4. zrb/builtin/group.py +1 -0
  5. zrb/builtin/llm/llm_chat.py +2 -2
  6. zrb/builtin/llm/tool/web.py +1 -1
  7. zrb/builtin/project/add/fastapp/fastapp_task.py +2 -0
  8. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/config.py +5 -2
  9. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_task.py +80 -20
  10. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +150 -42
  11. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/my_entity_service.py +113 -0
  12. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/my_entity_service_factory.py +9 -0
  13. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/repository/my_entity_db_repository.py +0 -10
  14. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/repository/my_entity_repository.py +37 -16
  15. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/repository/{factory.py → my_entity_repository_factory.py} +2 -2
  16. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/schema/my_entity.py +16 -6
  17. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/client_method.py +57 -0
  18. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/gateway_subroute.py +72 -0
  19. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/format_task.py +1 -1
  20. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/input.py +13 -0
  21. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_task.py +23 -0
  22. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +42 -0
  23. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/gateway/subroute/my_module.py +7 -0
  24. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/my_module_api_client.py +6 -0
  25. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/{any_client.py → my_module_client.py} +1 -1
  26. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/my_module_client_factory.py +11 -0
  27. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/my_module_direct_client.py +5 -0
  28. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/route.py +11 -11
  29. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/module_task_definition.py +2 -2
  30. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/task.py +8 -8
  31. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/util.py +47 -20
  32. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/app_factory.py +29 -0
  33. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_db_repository.py +185 -101
  34. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_service.py +236 -0
  35. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/{db_engine.py → db_engine_factory.py} +1 -1
  36. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/error.py +12 -0
  37. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/logger_factory.py +10 -0
  38. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/parser_factory.py +7 -0
  39. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/app.py +47 -0
  40. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/parser.py +105 -0
  41. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/user_agent.py +58 -0
  42. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/view.py +37 -0
  43. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/config.py +25 -1
  44. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/main.py +1 -1
  45. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_api_client.py +16 -0
  46. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_client.py +163 -0
  47. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_client_factory.py +9 -0
  48. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_direct_client.py +15 -0
  49. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/3093c7336477_add_auth_tables.py +160 -0
  50. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration_metadata.py +18 -1
  51. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/route.py +7 -3
  52. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/permission_service.py +117 -0
  53. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/permission_service_factory.py +11 -0
  54. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/repository/permission_db_repository.py +26 -0
  55. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/repository/permission_repository.py +61 -0
  56. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/repository/permission_repository_factory.py +13 -0
  57. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_db_repository.py +75 -0
  58. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_repository.py +59 -0
  59. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_repository_factory.py +13 -0
  60. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/role_service.py +105 -0
  61. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/role_service_factory.py +7 -0
  62. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_db_repository.py +42 -13
  63. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_repository.py +38 -17
  64. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_repository_factory.py +2 -2
  65. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service.py +105 -0
  66. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service_factory.py +7 -0
  67. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/route.py +43 -14
  68. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/subroute/auth.py +198 -28
  69. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/util/view.py +74 -0
  70. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/error.html +6 -0
  71. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/homepage.html +6 -0
  72. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/images/android-chrome-192x192.png +0 -0
  73. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/images/android-chrome-512x512.png +0 -0
  74. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/images/favicon-32x32.png +0 -0
  75. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.amber.min.css +4 -0
  76. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.blue.min.css +4 -0
  77. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.cyan.min.css +4 -0
  78. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.fuchsia.min.css +4 -0
  79. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.green.min.css +4 -0
  80. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.grey.min.css +4 -0
  81. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.indigo.min.css +4 -0
  82. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.jade.min.css +4 -0
  83. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.lime.min.css +4 -0
  84. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.min.css +4 -0
  85. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.orange.min.css +4 -0
  86. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.pink.min.css +4 -0
  87. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.pumpkin.min.css +4 -0
  88. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.purple.min.css +4 -0
  89. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.red.min.css +4 -0
  90. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.sand.min.css +4 -0
  91. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.slate.min.css +4 -0
  92. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.violet.min.css +4 -0
  93. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.yellow.min.css +4 -0
  94. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.zinc.min.css +4 -0
  95. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/template/default.html +34 -0
  96. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/requirements.txt +1 -0
  97. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/permission.py +17 -5
  98. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/role.py +50 -4
  99. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/session.py +52 -0
  100. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/user.py +30 -5
  101. zrb/builtin/python.py +1 -1
  102. zrb/builtin/random.py +61 -0
  103. zrb/cmd/cmd_val.py +6 -5
  104. zrb/content_transformer/any_content_transformer.py +7 -0
  105. zrb/content_transformer/content_transformer.py +6 -0
  106. zrb/runner/cli.py +14 -7
  107. zrb/runner/web_app.py +28 -280
  108. zrb/runner/web_config/config.py +91 -0
  109. zrb/runner/web_config/config_factory.py +26 -0
  110. zrb/runner/web_route/docs_route.py +17 -0
  111. zrb/runner/web_route/error_page/serve_default_404.py +28 -0
  112. zrb/runner/{web_controller/error_page/controller.py → web_route/error_page/show_error_page.py} +2 -2
  113. zrb/runner/{web_controller → web_route}/error_page/view.html +5 -0
  114. zrb/runner/web_route/home_page/home_page_route.py +51 -0
  115. zrb/runner/web_route/login_api_route.py +31 -0
  116. zrb/runner/web_route/login_page/login_page_route.py +39 -0
  117. zrb/runner/web_route/logout_api_route.py +18 -0
  118. zrb/runner/web_route/logout_page/logout_page_route.py +40 -0
  119. zrb/runner/{web_controller/group_info_page/controller.py → web_route/node_page/group/show_group_page.py} +3 -3
  120. zrb/runner/web_route/node_page/node_page_route.py +50 -0
  121. zrb/runner/{web_controller/session_page/controller.py → web_route/node_page/task/show_task_page.py} +3 -3
  122. zrb/runner/web_route/refresh_token_api_route.py +38 -0
  123. zrb/runner/web_route/static/static_route.py +44 -0
  124. zrb/runner/web_route/task_input_api_route.py +47 -0
  125. zrb/runner/web_route/task_session_api_route.py +102 -0
  126. zrb/runner/web_schema/session.py +5 -0
  127. zrb/runner/web_schema/token.py +11 -0
  128. zrb/runner/web_schema/user.py +32 -0
  129. zrb/runner/web_util/cookie.py +29 -0
  130. zrb/runner/{web_util.py → web_util/html.py} +1 -23
  131. zrb/runner/web_util/token.py +72 -0
  132. zrb/runner/web_util/user.py +63 -0
  133. zrb/session/session.py +6 -4
  134. zrb/session_state_logger/{default_session_state_logger.py → session_state_logger_factory.py} +1 -1
  135. zrb/task/base_task.py +53 -6
  136. zrb/task/base_trigger.py +2 -0
  137. zrb/task/cmd_task.py +9 -5
  138. zrb/task/http_check.py +2 -0
  139. zrb/task/llm_task.py +2 -0
  140. zrb/task/make_task.py +2 -0
  141. zrb/task/rsync_task.py +2 -0
  142. zrb/task/scaffolder.py +8 -5
  143. zrb/task/scheduler.py +2 -0
  144. zrb/task/tcp_check.py +2 -0
  145. zrb/task_status/task_status.py +4 -3
  146. zrb/util/cmd/command.py +1 -0
  147. zrb/util/file.py +7 -1
  148. {zrb-1.0.0a21.dist-info → zrb-1.0.0b2.dist-info}/METADATA +1 -1
  149. zrb-1.0.0b2.dist-info/RECORD +307 -0
  150. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/any_client_method.py +0 -27
  151. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/my_entity_usecase.py +0 -65
  152. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/api_client.py +0 -6
  153. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/direct_client.py +0 -6
  154. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/factory.py +0 -9
  155. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/app.py +0 -20
  156. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_usecase.py +0 -245
  157. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/any_client.py +0 -33
  158. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/api_client.py +0 -7
  159. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/direct_client.py +0 -6
  160. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/factory.py +0 -9
  161. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/3093c7336477_add_user_table.py +0 -37
  162. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_usecase.py +0 -53
  163. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_usecase_factory.py +0 -6
  164. zrb/runner/web_config.py +0 -274
  165. zrb/runner/web_controller/home_page/controller.py +0 -33
  166. zrb/runner/web_controller/login_page/controller.py +0 -25
  167. zrb/runner/web_controller/logout_page/controller.py +0 -26
  168. zrb-1.0.0a21.dist-info/RECORD +0 -244
  169. /zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/{create_column_task.py → add_column_task.py} +0 -0
  170. /zrb/{runner/web_controller → builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission}/__init__.py +0 -0
  171. /zrb/{runner/web_controller/group_info_page → builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role}/__init__.py +0 -0
  172. /zrb/runner/{web_controller/home_page → web_route}/__init__.py +0 -0
  173. /zrb/runner/{web_controller/session_page → web_route/home_page}/__init__.py +0 -0
  174. /zrb/runner/{web_controller → web_route}/home_page/view.html +0 -0
  175. /zrb/runner/{web_controller → web_route}/login_page/view.html +0 -0
  176. /zrb/runner/{web_controller → web_route}/logout_page/view.html +0 -0
  177. /zrb/runner/{web_controller/group_info_page → web_route/node_page/group}/view.html +0 -0
  178. /zrb/runner/{web_controller/session_page → web_route/node_page/task}/partial/input.html +0 -0
  179. /zrb/runner/{web_controller/session_page → web_route/node_page/task}/view.html +0 -0
  180. /zrb/runner/{refresh-token.template.js → web_route/static/refresh-token.template.js} +0 -0
  181. /zrb/runner/{web_controller/static → web_route/static/resources}/common.css +0 -0
  182. /zrb/runner/{web_controller/static → web_route/static/resources}/favicon-32x32.png +0 -0
  183. /zrb/runner/{web_controller/static → web_route/static/resources}/login/event.js +0 -0
  184. /zrb/runner/{web_controller/static → web_route/static/resources}/logout/event.js +0 -0
  185. /zrb/runner/{web_controller/static → web_route/static/resources}/pico.min.css +0 -0
  186. /zrb/runner/{web_controller/static → web_route/static/resources}/session/common-util.js +0 -0
  187. /zrb/runner/{web_controller/static → web_route/static/resources}/session/current-session.js +0 -0
  188. /zrb/runner/{web_controller/static → web_route/static/resources}/session/event.js +0 -0
  189. /zrb/runner/{web_controller/static → web_route/static/resources}/session/past-session.js +0 -0
  190. {zrb-1.0.0a21.dist-info → zrb-1.0.0b2.dist-info}/WHEEL +0 -0
  191. {zrb-1.0.0a21.dist-info → zrb-1.0.0b2.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,75 @@
1
+ from typing import Any
2
+
3
+ from my_app_name.common.base_db_repository import BaseDBRepository
4
+ from my_app_name.module.auth.service.role.repository.role_repository import (
5
+ RoleRepository,
6
+ )
7
+ from my_app_name.schema.permission import Permission
8
+ from my_app_name.schema.role import (
9
+ Role,
10
+ RoleCreateWithAudit,
11
+ RolePermission,
12
+ RoleResponse,
13
+ RoleUpdateWithAudit,
14
+ )
15
+ from sqlalchemy.sql import Select
16
+ from sqlmodel import select
17
+
18
+
19
+ class RoleDBRepository(
20
+ BaseDBRepository[
21
+ Role,
22
+ RoleResponse,
23
+ RoleCreateWithAudit,
24
+ RoleUpdateWithAudit,
25
+ ],
26
+ RoleRepository,
27
+ ):
28
+ db_model = Role
29
+ response_model = RoleResponse
30
+ create_model = RoleCreateWithAudit
31
+ update_model = RoleUpdateWithAudit
32
+ entity_name = "role"
33
+
34
+ def _select(self) -> Select:
35
+ return (
36
+ select(Role, Permission)
37
+ .join(RolePermission, RolePermission.role_id == Role.id, isouter=True)
38
+ .join(Permission, Permission.id == RolePermission.role_id, isouter=True)
39
+ )
40
+
41
+ def _rows_to_responses(self, rows: list[tuple[Role, Permission]]) -> RoleResponse:
42
+ role_map: dict[str, dict[str, Any]] = {}
43
+ for role, permission in rows:
44
+ if role.id not in role_map:
45
+ role_map[role.id] = {"role": role, "permissions": set()}
46
+ if permission:
47
+ role_map[role.id]["permissions"].add(permission)
48
+ return [
49
+ RoleResponse(
50
+ **data["role"].model_dump(), permissions=list(data["permissions"])
51
+ )
52
+ for data in role_map.values()
53
+ ]
54
+
55
+ async def add_permissions(self, role_id, permission_names: list[str]) -> Role:
56
+ # TODO: complete this
57
+ select_statement = self._select().where(self.db_model.id == role_id)
58
+ rows = await self._execute_select_statement(select_statement)
59
+ responses = self._rows_to_responses(rows)
60
+ return self._ensure_one(responses)
61
+
62
+ async def remove_permissions(self, role_id, permission_names: list[str]) -> Role:
63
+ # TODO: complete this
64
+ select_statement = self._select().where(self.db_model.id == role_id)
65
+ rows = await self._execute_select_statement(select_statement)
66
+ responses = self._rows_to_responses(rows)
67
+ return self._ensure_one(responses)
68
+
69
+
70
+ def _remove_create_model_additional_property(
71
+ data: RoleCreateWithAudit,
72
+ ) -> RoleCreateWithAudit:
73
+ return RoleCreateWithAudit(
74
+ **{key: val for key, val in data.model_dump().items() if key != "permissions"}
75
+ )
@@ -0,0 +1,59 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ from my_app_name.schema.role import (
4
+ Role,
5
+ RoleCreateWithAudit,
6
+ RoleResponse,
7
+ RoleUpdateWithAudit,
8
+ )
9
+
10
+
11
+ class RoleRepository(ABC):
12
+
13
+ @abstractmethod
14
+ async def get_by_id(self, id: str) -> RoleResponse:
15
+ """Get role by id"""
16
+
17
+ @abstractmethod
18
+ async def get_by_ids(self, id_list: list[str]) -> RoleResponse:
19
+ """Get roles by ids"""
20
+
21
+ @abstractmethod
22
+ async def get(
23
+ self,
24
+ page: int = 1,
25
+ page_size: int = 10,
26
+ filter: str | None = None,
27
+ sort: str | None = None,
28
+ ) -> list[Role]:
29
+ """Get roles by filter and sort"""
30
+
31
+ @abstractmethod
32
+ async def count(self, filter: str | None = None) -> int:
33
+ """Count roles by filter"""
34
+
35
+ @abstractmethod
36
+ async def create(self, data: RoleCreateWithAudit) -> Role:
37
+ """Create a new role"""
38
+
39
+ @abstractmethod
40
+ async def create_bulk(self, data: list[RoleCreateWithAudit]) -> list[Role]:
41
+ """Create some roles"""
42
+
43
+ @abstractmethod
44
+ async def delete(self, id: str) -> Role:
45
+ """Delete a role"""
46
+
47
+ @abstractmethod
48
+ async def delete_bulk(self, id_list: list[str]) -> list[Role]:
49
+ """Delete some roles"""
50
+
51
+ @abstractmethod
52
+ async def update(self, id: str, data: RoleUpdateWithAudit) -> Role:
53
+ """Update a role"""
54
+
55
+ @abstractmethod
56
+ async def update_bulk(
57
+ self, id_list: list[str], data: RoleUpdateWithAudit
58
+ ) -> list[Role]:
59
+ """Update some roles"""
@@ -0,0 +1,13 @@
1
+ from my_app_name.common.db_engine_factory import db_engine
2
+ from my_app_name.config import APP_REPOSITORY_TYPE
3
+ from my_app_name.module.auth.service.role.repository.role_db_repository import (
4
+ RoleDBRepository,
5
+ )
6
+ from my_app_name.module.auth.service.role.repository.role_repository import (
7
+ RoleRepository,
8
+ )
9
+
10
+ if APP_REPOSITORY_TYPE == "db":
11
+ role_repository: RoleRepository = RoleDBRepository(db_engine)
12
+ else:
13
+ role_repository: RoleRepository = None
@@ -0,0 +1,105 @@
1
+ from logging import Logger
2
+
3
+ from my_app_name.common.base_service import BaseService
4
+ from my_app_name.module.auth.service.role.repository.role_repository import (
5
+ RoleRepository,
6
+ )
7
+ from my_app_name.schema.role import (
8
+ MultipleRoleResponse,
9
+ RoleCreateWithAudit,
10
+ RoleResponse,
11
+ RoleUpdateWithAudit,
12
+ )
13
+
14
+
15
+ class RoleService(BaseService):
16
+
17
+ def __init__(self, logger: Logger, role_repository: RoleRepository):
18
+ super().__init__(logger)
19
+ self.role_repository = role_repository
20
+
21
+ @BaseService.route(
22
+ "/api/v1/roles/{role_id}",
23
+ methods=["get"],
24
+ response_model=RoleResponse,
25
+ )
26
+ async def get_role_by_id(self, role_id: str) -> RoleResponse:
27
+ return await self.role_repository.get_by_id(role_id)
28
+
29
+ @BaseService.route(
30
+ "/api/v1/roles",
31
+ methods=["get"],
32
+ response_model=MultipleRoleResponse,
33
+ )
34
+ async def get_roles(
35
+ self,
36
+ page: int = 1,
37
+ page_size: int = 10,
38
+ sort: str | None = None,
39
+ filter: str | None = None,
40
+ ) -> MultipleRoleResponse:
41
+ roles = await self.role_repository.get(page, page_size, filter, sort)
42
+ count = await self.role_repository.count(filter)
43
+ return MultipleRoleResponse(data=roles, count=count)
44
+
45
+ @BaseService.route(
46
+ "/api/v1/roles",
47
+ methods=["post"],
48
+ response_model=RoleResponse,
49
+ )
50
+ async def create_role(self, data: RoleCreateWithAudit) -> RoleResponse:
51
+ role = await self.role_repository.create(data)
52
+ return await self.role_repository.get_by_id(role.id)
53
+
54
+ @BaseService.route(
55
+ "/api/v1/roles/bulk",
56
+ methods=["post"],
57
+ response_model=list[RoleResponse],
58
+ )
59
+ async def create_role_bulk(
60
+ self, data: list[RoleCreateWithAudit]
61
+ ) -> list[RoleResponse]:
62
+ roles = await self.role_repository.create_bulk(data)
63
+ return await self.role_repository.get_by_ids([role.id for role in roles])
64
+
65
+ @BaseService.route(
66
+ "/api/v1/roles/bulk",
67
+ methods=["put"],
68
+ response_model=RoleResponse,
69
+ )
70
+ async def update_role_bulk(
71
+ self, role_ids: list[str], data: RoleUpdateWithAudit
72
+ ) -> RoleResponse:
73
+ roles = await self.role_repository.update_bulk(role_ids, data)
74
+ return await self.role_repository.get_by_ids([role.id for role in roles])
75
+
76
+ @BaseService.route(
77
+ "/api/v1/roles/{role_id}",
78
+ methods=["put"],
79
+ response_model=RoleResponse,
80
+ )
81
+ async def update_role(
82
+ self, role_id: str, data: RoleUpdateWithAudit
83
+ ) -> RoleResponse:
84
+ role = await self.role_repository.update(role_id, data)
85
+ return await self.role_repository.get_by_id(role.id)
86
+
87
+ @BaseService.route(
88
+ "/api/v1/roles/{role_id}",
89
+ methods=["delete"],
90
+ response_model=RoleResponse,
91
+ )
92
+ async def delete_role_bulk(
93
+ self, role_ids: list[str], deleted_by: str
94
+ ) -> RoleResponse:
95
+ roles = await self.role_repository.delete_bulk(role_ids)
96
+ return await self.role_repository.get_by_ids([role.id for role in roles])
97
+
98
+ @BaseService.route(
99
+ "/api/v1/roles/{role_id}",
100
+ methods=["delete"],
101
+ response_model=RoleResponse,
102
+ )
103
+ async def delete_role(self, role_id: str, deleted_by: str) -> RoleResponse:
104
+ role = await self.role_repository.delete(role_id)
105
+ return await self.role_repository.get_by_id(role.id)
@@ -0,0 +1,7 @@
1
+ from my_app_name.common.logger_factory import logger
2
+ from my_app_name.module.auth.service.role.repository.role_repository_factory import (
3
+ role_repository,
4
+ )
5
+ from my_app_name.module.auth.service.role.role_service import RoleService
6
+
7
+ role_service = RoleService(logger, role_repository=role_repository)
@@ -1,17 +1,21 @@
1
+ from typing import Any
2
+
1
3
  from my_app_name.common.base_db_repository import BaseDBRepository
2
- from my_app_name.common.error import NotFoundError
3
4
  from my_app_name.module.auth.service.user.repository.user_repository import (
4
5
  UserRepository,
5
6
  )
7
+ from my_app_name.schema.permission import Permission
8
+ from my_app_name.schema.role import Role, RolePermission
6
9
  from my_app_name.schema.user import (
7
10
  User,
8
11
  UserCreateWithAudit,
9
12
  UserResponse,
13
+ UserRole,
10
14
  UserUpdateWithAudit,
11
15
  )
12
16
  from passlib.context import CryptContext
13
- from sqlalchemy.ext.asyncio import AsyncSession
14
- from sqlmodel import Session, select
17
+ from sqlalchemy.sql import Select
18
+ from sqlmodel import select
15
19
 
16
20
  # Password hashing context
17
21
  pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
@@ -32,14 +36,39 @@ class UserDBRepository(
32
36
  entity_name = "user"
33
37
  column_preprocessors = {"password": hash_password}
34
38
 
39
+ def _select(self) -> Select:
40
+ return (
41
+ select(User, Role, Permission)
42
+ .join(UserRole, UserRole.user_id == User.id, isouter=True)
43
+ .join(Role, Role.id == UserRole.role_id, isouter=True)
44
+ .join(RolePermission, RolePermission.role_id == Role.id, isouter=True)
45
+ .join(Permission, Permission.id == RolePermission.role_id, isouter=True)
46
+ )
47
+
48
+ def _rows_to_responses(
49
+ self, rows: list[tuple[User, Role, Permission]]
50
+ ) -> UserResponse:
51
+ user_map: dict[str, dict[str, Any]] = {}
52
+ for user, role, permission in rows:
53
+ if user.id not in user_map:
54
+ user_map[user.id] = {"user": user, "roles": set(), "permissions": set()}
55
+ if role:
56
+ user_map[user.id]["roles"].add(role)
57
+ if permission:
58
+ user_map[user.id]["permissions"].add(permission)
59
+ return [
60
+ UserResponse(
61
+ **data["user"].model_dump(),
62
+ roles=list(data["roles"]),
63
+ permissions=list(data["permissions"])
64
+ )
65
+ for data in user_map.values()
66
+ ]
67
+
35
68
  async def get_by_credentials(self, username: str, password: str) -> UserResponse:
36
- statement = select(User).where(User.username == username)
37
- if self.is_async:
38
- async with AsyncSession(self.engine) as session:
39
- user = await session.exec(statement).first
40
- else:
41
- with Session(self.engine) as session:
42
- user = session.exec(statement).first()
43
- if not user or not pwd_context.verify(password, user.hashed_password):
44
- raise NotFoundError(f"{self.entity_name} not found")
45
- return self._to_response(user)
69
+ select_statement = self._select().where(
70
+ User.username == username, User.password == hash_password(password)
71
+ )
72
+ rows = await self._execute_select_statement(select_statement)
73
+ responses = self._rows_to_responses(rows)
74
+ return self._ensure_one(responses)
@@ -9,34 +9,55 @@ from my_app_name.schema.user import (
9
9
 
10
10
 
11
11
  class UserRepository(ABC):
12
+
13
+ @abstractmethod
14
+ async def get_by_id(self, id: str) -> UserResponse:
15
+ """Get user by id"""
16
+
17
+ @abstractmethod
18
+ async def get_by_ids(self, id_list: list[str]) -> UserResponse:
19
+ """Get users by ids"""
20
+
21
+ @abstractmethod
22
+ async def get(
23
+ self,
24
+ page: int = 1,
25
+ page_size: int = 10,
26
+ filter: str | None = None,
27
+ sort: str | None = None,
28
+ ) -> list[User]:
29
+ """Get users by filter and sort"""
30
+
31
+ @abstractmethod
32
+ async def count(self, filter: str | None = None) -> int:
33
+ """Count users by filter"""
34
+
12
35
  @abstractmethod
13
- async def create(self, user_data: UserCreateWithAudit) -> UserResponse:
14
- pass
36
+ async def create(self, data: UserCreateWithAudit) -> User:
37
+ """Create a new user"""
15
38
 
16
39
  @abstractmethod
17
- async def get_by_id(self, user_id: str) -> UserResponse:
18
- pass
40
+ async def create_bulk(self, data: list[UserCreateWithAudit]) -> list[User]:
41
+ """Create some users"""
19
42
 
20
43
  @abstractmethod
21
- async def get_all(self) -> list[User]:
22
- pass
44
+ async def delete(self, id: str) -> User:
45
+ """Delete a user"""
23
46
 
24
47
  @abstractmethod
25
- async def update(
26
- self, user_id: str, user_data: UserUpdateWithAudit
27
- ) -> UserResponse:
28
- pass
48
+ async def delete_bulk(self, id_list: list[str]) -> list[User]:
49
+ """Delete some users"""
29
50
 
30
51
  @abstractmethod
31
- async def delete(self, user_id: str) -> UserResponse:
32
- pass
52
+ async def update(self, id: str, data: UserUpdateWithAudit) -> User:
53
+ """Update a user"""
33
54
 
34
55
  @abstractmethod
35
- async def create_bulk(
36
- self, user_data_list: list[UserCreateWithAudit]
37
- ) -> list[UserResponse]:
38
- pass
56
+ async def update_bulk(
57
+ self, id_list: list[str], data: UserUpdateWithAudit
58
+ ) -> list[User]:
59
+ """Update some users"""
39
60
 
40
61
  @abstractmethod
41
62
  async def get_by_credentials(self, username: str, password: str) -> UserResponse:
42
- pass
63
+ """Get user by credential"""
@@ -1,4 +1,4 @@
1
- from my_app_name.common.db_engine import engine
1
+ from my_app_name.common.db_engine_factory import db_engine
2
2
  from my_app_name.config import APP_REPOSITORY_TYPE
3
3
  from my_app_name.module.auth.service.user.repository.user_db_repository import (
4
4
  UserDBRepository,
@@ -8,6 +8,6 @@ from my_app_name.module.auth.service.user.repository.user_repository import (
8
8
  )
9
9
 
10
10
  if APP_REPOSITORY_TYPE == "db":
11
- user_repository: UserRepository = UserDBRepository(engine)
11
+ user_repository: UserRepository = UserDBRepository(db_engine)
12
12
  else:
13
13
  user_repository: UserRepository = None
@@ -0,0 +1,105 @@
1
+ from logging import Logger
2
+
3
+ from my_app_name.common.base_service import BaseService
4
+ from my_app_name.module.auth.service.user.repository.user_repository import (
5
+ UserRepository,
6
+ )
7
+ from my_app_name.schema.user import (
8
+ MultipleUserResponse,
9
+ UserCreateWithAudit,
10
+ UserResponse,
11
+ UserUpdateWithAudit,
12
+ )
13
+
14
+
15
+ class UserService(BaseService):
16
+
17
+ def __init__(self, logger: Logger, user_repository: UserRepository):
18
+ super().__init__(logger)
19
+ self.user_repository = user_repository
20
+
21
+ @BaseService.route(
22
+ "/api/v1/users/{user_id}",
23
+ methods=["get"],
24
+ response_model=UserResponse,
25
+ )
26
+ async def get_user_by_id(self, user_id: str) -> UserResponse:
27
+ return await self.user_repository.get_by_id(user_id)
28
+
29
+ @BaseService.route(
30
+ "/api/v1/users",
31
+ methods=["get"],
32
+ response_model=MultipleUserResponse,
33
+ )
34
+ async def get_users(
35
+ self,
36
+ page: int = 1,
37
+ page_size: int = 10,
38
+ sort: str | None = None,
39
+ filter: str | None = None,
40
+ ) -> MultipleUserResponse:
41
+ users = await self.user_repository.get(page, page_size, filter, sort)
42
+ count = await self.user_repository.count(filter)
43
+ return MultipleUserResponse(data=users, count=count)
44
+
45
+ @BaseService.route(
46
+ "/api/v1/users",
47
+ methods=["post"],
48
+ response_model=UserResponse,
49
+ )
50
+ async def create_user(self, data: UserCreateWithAudit) -> UserResponse:
51
+ user = await self.user_repository.create(data)
52
+ return await self.user_repository.get_by_id(user.id)
53
+
54
+ @BaseService.route(
55
+ "/api/v1/users/bulk",
56
+ methods=["post"],
57
+ response_model=list[UserResponse],
58
+ )
59
+ async def create_user_bulk(
60
+ self, data: list[UserCreateWithAudit]
61
+ ) -> list[UserResponse]:
62
+ users = await self.user_repository.create_bulk(data)
63
+ return await self.user_repository.get_by_ids([user.id for user in users])
64
+
65
+ @BaseService.route(
66
+ "/api/v1/users/bulk",
67
+ methods=["put"],
68
+ response_model=UserResponse,
69
+ )
70
+ async def update_user_bulk(
71
+ self, user_ids: list[str], data: UserUpdateWithAudit
72
+ ) -> UserResponse:
73
+ users = await self.user_repository.update_bulk(user_ids, data)
74
+ return await self.user_repository.get_by_ids([user.id for user in users])
75
+
76
+ @BaseService.route(
77
+ "/api/v1/users/{user_id}",
78
+ methods=["put"],
79
+ response_model=UserResponse,
80
+ )
81
+ async def update_user(
82
+ self, user_id: str, data: UserUpdateWithAudit
83
+ ) -> UserResponse:
84
+ user = await self.user_repository.update(user_id, data)
85
+ return await self.user_repository.get_by_id(user.id)
86
+
87
+ @BaseService.route(
88
+ "/api/v1/users/{user_id}",
89
+ methods=["delete"],
90
+ response_model=UserResponse,
91
+ )
92
+ async def delete_user_bulk(
93
+ self, user_ids: list[str], deleted_by: str
94
+ ) -> UserResponse:
95
+ users = await self.user_repository.delete_bulk(user_ids)
96
+ return await self.user_repository.get_by_ids([user.id for user in users])
97
+
98
+ @BaseService.route(
99
+ "/api/v1/users/{user_id}",
100
+ methods=["delete"],
101
+ response_model=UserResponse,
102
+ )
103
+ async def delete_user(self, user_id: str, deleted_by: str) -> UserResponse:
104
+ user = await self.user_repository.delete(user_id)
105
+ return await self.user_repository.get_by_id(user.id)
@@ -0,0 +1,7 @@
1
+ from my_app_name.common.logger_factory import logger
2
+ from my_app_name.module.auth.service.user.repository.user_repository_factory import (
3
+ user_repository,
4
+ )
5
+ from my_app_name.module.auth.service.user.user_service import UserService
6
+
7
+ user_service = UserService(logger, user_repository=user_repository)
@@ -1,11 +1,42 @@
1
- from fastapi import FastAPI
2
- from my_app_name.common.app import app
1
+ import os
2
+
3
+ from fastapi import FastAPI, HTTPException, Request
4
+ from fastapi.exception_handlers import http_exception_handler
5
+ from fastapi.responses import HTMLResponse
6
+ from my_app_name.common.app_factory import app
3
7
  from my_app_name.common.schema import BasicResponse
4
- from my_app_name.config import APP_MAIN_MODULE, APP_MODE, APP_MODULES
8
+ from my_app_name.config import (
9
+ APP_GATEWAY_VIEW_PATH,
10
+ APP_MAIN_MODULE,
11
+ APP_MODE,
12
+ APP_MODULES,
13
+ )
5
14
  from my_app_name.module.gateway.subroute.auth import serve_auth_route
15
+ from my_app_name.module.gateway.util.view import render, render_error
16
+
17
+
18
+ def serve_route(app: FastAPI):
19
+ if APP_MODE != "monolith" and "gateway" not in APP_MODULES:
20
+ return
21
+ if APP_MODE == "monolith" or APP_MAIN_MODULE == "gateway":
22
+ _serve_health_check(app)
23
+ _serve_readiness_check(app)
24
+ _serve_homepage(app)
25
+ _handle_404(app)
26
+
27
+ # Serve auth routes
28
+ serve_auth_route(app)
6
29
 
7
30
 
8
- def serve_health_check(app: FastAPI):
31
+ def _serve_homepage(app: FastAPI):
32
+ @app.get("/", include_in_schema=False)
33
+ def home_page():
34
+ return render(
35
+ view_path=os.path.join(APP_GATEWAY_VIEW_PATH, "content", "homepage.html")
36
+ )
37
+
38
+
39
+ def _serve_health_check(app: FastAPI):
9
40
  @app.api_route("/health", methods=["GET", "HEAD"], response_model=BasicResponse)
10
41
  async def health():
11
42
  """
@@ -14,7 +45,7 @@ def serve_health_check(app: FastAPI):
14
45
  return BasicResponse(message="ok")
15
46
 
16
47
 
17
- def serve_readiness_check(app: FastAPI):
48
+ def _serve_readiness_check(app: FastAPI):
18
49
  @app.api_route("/readiness", methods=["GET", "HEAD"], response_model=BasicResponse)
19
50
  async def readiness():
20
51
  """
@@ -23,15 +54,13 @@ def serve_readiness_check(app: FastAPI):
23
54
  return BasicResponse(message="ok")
24
55
 
25
56
 
26
- def serve_route(app: FastAPI):
27
- if APP_MODE != "monolith" and "gateway" not in APP_MODULES:
28
- return
29
- if APP_MODE == "monolith" or APP_MAIN_MODULE == "gateway":
30
- serve_health_check(app)
31
- serve_readiness_check(app)
32
-
33
- # Serve Auth Route
34
- serve_auth_route(app)
57
+ def _handle_404(app: FastAPI):
58
+ @app.exception_handler(404)
59
+ async def default_404(request: Request, exc: HTTPException) -> HTMLResponse:
60
+ if request.url.path.startswith("/api"):
61
+ # Re-raise the exception to let FastAPI handle it
62
+ return await http_exception_handler(request, exc)
63
+ return render_error(error_message="Not found", status_code=404)
35
64
 
36
65
 
37
66
  serve_route(app)