zrb 1.0.0a15__py3-none-any.whl → 1.0.0a17__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 (121) hide show
  1. zrb/__main__.py +3 -0
  2. zrb/builtin/__init__.py +2 -2
  3. zrb/builtin/git.py +16 -8
  4. zrb/builtin/git_subtree.py +7 -2
  5. zrb/builtin/llm/tool/rag.py +2 -2
  6. zrb/builtin/project/add/fastapp/fastapp_input.py +16 -0
  7. zrb/builtin/project/add/fastapp/fastapp_task.py +92 -0
  8. zrb/builtin/project/add/fastapp/fastapp_template/_zrb/entity/any_client_method.template.py +27 -0
  9. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/_zrb/entity/module_template/service/my_entity/my_entity_usecase.py +6 -6
  10. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/_zrb/entity/module_template/service/my_entity/repository/my_entity_db_repository.py +10 -5
  11. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/_zrb/entity/module_template/service/my_entity/repository/my_entity_repository.py +5 -5
  12. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/_zrb/entity/schema.template.py +8 -0
  13. zrb/builtin/project/add/{fastapp_template/_zrb/entity/create_entity_task.py → fastapp/fastapp_template/_zrb/entity/task.py} +106 -42
  14. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/_zrb/input.py +1 -1
  15. zrb/builtin/project/add/{fastapp_template/_zrb/module/create_module_task.py → fastapp/fastapp_template/_zrb/module/task.py} +38 -31
  16. zrb/builtin/project/add/fastapp/fastapp_template/_zrb/module/template/module_template/client/any_client.py +7 -0
  17. zrb/builtin/project/add/fastapp/fastapp_template/_zrb/module/template/module_template/client/api_client.py +6 -0
  18. zrb/builtin/project/add/{fastapp_template/_zrb/module → fastapp/fastapp_template/_zrb/module/template}/module_template/client/direct_client.py +2 -2
  19. zrb/builtin/project/add/{fastapp_template/_zrb/module → fastapp/fastapp_template/_zrb/module/template}/module_template/client/factory.py +3 -3
  20. zrb/builtin/project/add/fastapp/fastapp_template/_zrb/module/template/module_template/route.py +33 -0
  21. zrb/builtin/project/add/{fastapp_template/_zrb/main.py → fastapp/fastapp_template/_zrb/task.py} +3 -3
  22. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/common/base_usecase.py +19 -6
  23. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/config.py +1 -0
  24. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/client/any_client.py +10 -4
  25. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/client/api_client.py +2 -2
  26. zrb/builtin/project/add/fastapp/fastapp_template/module/auth/client/direct_client.py +6 -0
  27. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/client/factory.py +3 -3
  28. zrb/builtin/project/add/fastapp/fastapp_template/module/auth/route.py +37 -0
  29. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/service/user/repository/user_db_repository.py +10 -4
  30. zrb/builtin/project/add/fastapp/fastapp_template/module/auth/service/user/repository/user_repository.py +43 -0
  31. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/service/user/user_usecase.py +10 -4
  32. zrb/builtin/project/add/fastapp/fastapp_template/module/gateway/route.py +43 -0
  33. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/requirements.txt +1 -1
  34. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/schema/permission.py +8 -0
  35. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/schema/role.py +8 -0
  36. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/schema/user.py +8 -0
  37. zrb/builtin/project/add/fastapp/fastapp_util.py +22 -0
  38. zrb/builtin/project/create/{create.py → project_task.py} +1 -1
  39. zrb/builtin/setup/asdf/asdf_helper.py +4 -8
  40. zrb/builtin/setup/tmux/tmux.py +7 -12
  41. zrb/builtin/todo.py +14 -19
  42. zrb/cmd/cmd_val.py +2 -2
  43. zrb/content_transformer/content_transformer.py +3 -4
  44. zrb/input/base_input.py +13 -1
  45. zrb/input/text_input.py +4 -4
  46. zrb/runner/web_controller/group_info_ui/controller.py +6 -14
  47. zrb/runner/web_controller/home_page/controller.py +6 -14
  48. zrb/runner/web_controller/task_ui/controller.py +11 -18
  49. zrb/session_state_logger/file_session_state_logger.py +4 -7
  50. zrb/task/cmd_task.py +1 -1
  51. zrb/task/llm_task.py +3 -5
  52. zrb/task/scaffolder.py +13 -1
  53. zrb/util/codemod/{add_code_to_class.py → append_code_to_class.py} +1 -1
  54. zrb/util/codemod/{add_code_to_function.py → append_code_to_function.py} +3 -1
  55. zrb/util/codemod/{add_code_to_method.py → append_code_to_method.py} +1 -1
  56. zrb/util/codemod/{add_key_to_dict.py → append_key_to_dict.py} +1 -1
  57. zrb/util/codemod/{add_param_to_function_call.py → append_param_to_function_call.py} +1 -1
  58. zrb/util/codemod/{add_code_to_module.py → prepend_code_to_module.py} +1 -1
  59. zrb/util/codemod/{add_parent_to_class.py → prepend_parent_to_class.py} +1 -1
  60. zrb/util/codemod/{add_property_to_class.py → prepend_property_to_class.py} +1 -1
  61. zrb/util/file.py +18 -0
  62. zrb/util/git_subtree.py +3 -4
  63. zrb/util/todo.py +5 -7
  64. {zrb-1.0.0a15.dist-info → zrb-1.0.0a17.dist-info}/METADATA +2 -2
  65. zrb-1.0.0a17.dist-info/RECORD +234 -0
  66. zrb/builtin/project/add/fastapp.py +0 -87
  67. zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/client/any_client.py +0 -27
  68. zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/client/api_client.py +0 -6
  69. zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/route.py +0 -19
  70. zrb/builtin/project/add/fastapp_template/module/auth/client/direct_client.py +0 -6
  71. zrb/builtin/project/add/fastapp_template/module/auth/migration/versions/3093c7336477_add_user_table.py +0 -37
  72. zrb/builtin/project/add/fastapp_template/module/auth/route.py +0 -22
  73. zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/user_repository.py +0 -34
  74. zrb/builtin/project/add/fastapp_template/module/gateway/route.py +0 -27
  75. zrb-1.0.0a15.dist-info/RECORD +0 -231
  76. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/.gitignore +0 -0
  77. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/README.md +0 -0
  78. /zrb/builtin/project/add/{__init__.py → fastapp/fastapp_template/__init__.py} +0 -0
  79. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/_zrb/column/create_column_task.py +0 -0
  80. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/_zrb/config.py +0 -0
  81. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/_zrb/entity/module_template/service/my_entity/repository/factory.py +0 -0
  82. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/_zrb/group.py +0 -0
  83. /zrb/builtin/project/add/{fastapp_template/_zrb/module → fastapp/fastapp_template/_zrb/module/template}/module_template/alembic.ini +0 -0
  84. /zrb/builtin/project/add/{fastapp_template/_zrb/module → fastapp/fastapp_template/_zrb/module/template}/module_template/migration/README +0 -0
  85. /zrb/builtin/project/add/{fastapp_template/_zrb/module → fastapp/fastapp_template/_zrb/module/template}/module_template/migration/env.py +0 -0
  86. /zrb/builtin/project/add/{fastapp_template/_zrb/module → fastapp/fastapp_template/_zrb/module/template}/module_template/migration/script.py.mako +0 -0
  87. /zrb/builtin/project/add/{fastapp_template/module/gateway → fastapp/fastapp_template/_zrb/module/template/module_template}/migration/versions/.gitkeep +0 -0
  88. /zrb/builtin/project/add/{fastapp_template/_zrb/module → fastapp/fastapp_template/_zrb/module/template}/module_template/migration_metadata.py +0 -0
  89. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/_zrb/module/template/module_template/service}/__init__.py +0 -0
  90. /zrb/builtin/project/add/{fastapp_template/_zrb/module/run_module.template.py → fastapp/fastapp_template/_zrb/module/template/task_definition.py} +0 -0
  91. /zrb/builtin/project/add/{fastapp_template/_zrb/helper.py → fastapp/fastapp_template/_zrb/util.py} +0 -0
  92. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/_zrb/venv_task.py +0 -0
  93. /zrb/builtin/project/add/{fastapp_template/_zrb/module/module_template/service → fastapp/fastapp_template/common}/__init__.py +0 -0
  94. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/common/app.py +0 -0
  95. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/common/base_db_repository.py +0 -0
  96. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/common/db_engine.py +0 -0
  97. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/common/error.py +0 -0
  98. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/common/schema.py +0 -0
  99. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/main.py +0 -0
  100. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/migrate.py +0 -0
  101. /zrb/builtin/project/add/{fastapp_template/common → fastapp/fastapp_template/module}/__init__.py +0 -0
  102. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/alembic.ini +0 -0
  103. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/migration/README +0 -0
  104. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/migration/env.py +0 -0
  105. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/migration/script.py.mako +0 -0
  106. /zrb/builtin/project/add/{fastapp_template/_zrb/module/module_template → fastapp/fastapp_template/module/auth}/migration/versions/3093c7336477_add_user_table.py +0 -0
  107. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/migration_metadata.py +0 -0
  108. /zrb/builtin/project/add/{fastapp_template/module → fastapp/fastapp_template/module/auth/service}/__init__.py +0 -0
  109. /zrb/builtin/project/add/{fastapp_template/module/auth/service → fastapp/fastapp_template/module/auth/service/user}/__init__.py +0 -0
  110. /zrb/builtin/project/add/{fastapp_template/module/auth/service/user → fastapp/fastapp_template/module/auth/service/user/repository}/__init__.py +0 -0
  111. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/auth/service/user/repository/factory.py +0 -0
  112. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/gateway/alembic.ini +0 -0
  113. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/gateway/migration/README +0 -0
  114. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/gateway/migration/env.py +0 -0
  115. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/gateway/migration/script.py.mako +0 -0
  116. /zrb/builtin/project/add/{fastapp_template/module/auth/service/user/repository/__init__.py → fastapp/fastapp_template/module/gateway/migration/versions/.gitkeep} +0 -0
  117. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/module/gateway/migration_metadata.py +0 -0
  118. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/schema/__init__.py +0 -0
  119. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template}/template.env +0 -0
  120. {zrb-1.0.0a15.dist-info → zrb-1.0.0a17.dist-info}/WHEEL +0 -0
  121. {zrb-1.0.0a15.dist-info → zrb-1.0.0a17.dist-info}/entry_points.txt +0 -0
@@ -2,10 +2,11 @@ import os
2
2
 
3
3
  from fastapp_template._zrb.config import APP_DIR
4
4
  from fastapp_template._zrb.group import app_create_group
5
- from fastapp_template._zrb.helper import get_existing_module_names
6
5
  from fastapp_template._zrb.input import new_module_input
6
+ from fastapp_template._zrb.util import get_existing_module_names
7
7
 
8
8
  from zrb import AnyContext, Scaffolder, Task, make_task
9
+ from zrb.util.file import read_file, write_file
9
10
  from zrb.util.string.conversion import to_kebab_case, to_pascal_case, to_snake_case
10
11
 
11
12
 
@@ -22,7 +23,7 @@ async def validate_create_my_app_name_module(ctx: AnyContext):
22
23
  scaffold_my_app_name_module = Scaffolder(
23
24
  name="scaffold-my-app-name-module",
24
25
  input=new_module_input,
25
- source_path=os.path.join(os.path.dirname(__file__), "module_template"),
26
+ source_path=os.path.join(os.path.dirname(__file__), "template", "module_template"),
26
27
  render_source_path=False,
27
28
  destination_path=lambda ctx: os.path.join(
28
29
  APP_DIR,
@@ -31,7 +32,8 @@ scaffold_my_app_name_module = Scaffolder(
31
32
  ),
32
33
  transform_content={
33
34
  "module_template": "{to_snake_case(ctx.input.module)}",
34
- "MY_MODULE_NAME": "{to_snake_case(ctx.input.module).upper()}",
35
+ "MY_MODULE": "{to_snake_case(ctx.input.module).upper()}",
36
+ "my_module": "{to_snake_case(ctx.input.module)}",
35
37
  },
36
38
  retries=0,
37
39
  upstream=validate_create_my_app_name_module,
@@ -60,8 +62,13 @@ async def register_my_app_name_module_config(ctx: AnyContext):
60
62
  config_name = f"APP_{upper_module_name}_BASE_URL"
61
63
  env_name = f"MY_APP_NAME_{upper_module_name}_BASE_URL"
62
64
  # TODO: check before write
63
- with open(config_file_name, "a") as f:
64
- f.write(f'{config_name} = os.getenv("{env_name}", "{module_base_url}")\n')
65
+ write_file(
66
+ config_file_name,
67
+ [
68
+ read_file(config_file_name),
69
+ f'{config_name} = os.getenv("{env_name}", "{module_base_url}")\n',
70
+ ],
71
+ )
65
72
 
66
73
 
67
74
  @make_task(
@@ -76,23 +83,24 @@ async def register_my_app_name_module(ctx: AnyContext):
76
83
  module_name = to_snake_case(ctx.input.module)
77
84
  import_code = f"from fastapp_template.module.{module_name} import route as {module_name}_route" # noqa
78
85
  assert_code = f"assert {module_name}_route"
79
- with open(app_main_file_name, "r") as f:
80
- code = f.read()
86
+ code = read_file(app_main_file_name)
81
87
  new_code = "\n".join([import_code, code.strip(), assert_code, ""])
82
88
  # TODO: check before write
83
- with open(app_main_file_name, "w") as f:
84
- f.write(new_code)
89
+ write_file(app_main_file_name, new_code)
90
+
91
+
92
+ # TODO: Register to zrb's config
85
93
 
86
94
 
87
95
  @make_task(
88
- name="register-my-app-name-module-runner",
96
+ name="add-my-app-name-module-task-to-zrb-init",
89
97
  input=new_module_input,
90
98
  upstream=validate_create_my_app_name_module,
91
99
  retries=0,
92
100
  )
93
- async def register_my_app_name_module_runner(ctx: AnyContext):
94
- """Registering module to _zrb's main.py"""
95
- task_main_file_name = os.path.join(APP_DIR, "_zrb", "main.py")
101
+ async def add_my_app_name_module_task_to_zrb_init(ctx: AnyContext):
102
+ """Registering module to _zrb's task.py"""
103
+ task_main_file_name = os.path.join(APP_DIR, "_zrb", "task.py")
96
104
  existing_module_names = get_existing_module_names()
97
105
  module_port = 3001 + len(
98
106
  [
@@ -104,33 +112,32 @@ async def register_my_app_name_module_runner(ctx: AnyContext):
104
112
  module_snake_name = to_snake_case(ctx.input.module)
105
113
  module_kebab_name = to_kebab_case(ctx.input.module)
106
114
  module_pascal_name = to_pascal_case(ctx.input.module)
107
- with open(os.path.join(os.path.dirname(__file__), "run_module.template.py")) as f:
108
- module_runner_code = (
109
- f.read()
110
- .replace("my_module", module_snake_name)
111
- .replace("my-module", module_kebab_name)
112
- .replace("My Module", module_pascal_name)
113
- .replace("3000", f"{module_port}")
114
- )
115
- with open(task_main_file_name, "r") as f:
116
- code = f.read()
115
+ module_runner_code = read_file(
116
+ os.path.join(os.path.dirname(__file__), "template", "task_definition.py"),
117
+ {
118
+ "my_module": module_snake_name,
119
+ "my-module": module_kebab_name,
120
+ "My Module": module_pascal_name,
121
+ "3000": f"{module_port}",
122
+ },
123
+ )
124
+ code = read_file(task_main_file_name)
117
125
  new_code = "\n".join([code.strip(), "", module_runner_code, ""])
118
126
  # TODO: check before write
119
- with open(task_main_file_name, "w") as f:
120
- f.write(new_code)
127
+ write_file(task_main_file_name, new_code)
121
128
 
122
129
 
123
130
  create_my_app_name_module = app_create_group.add_task(
124
131
  Task(
125
132
  name="create-my-app-name-module",
126
133
  description="🧩 Create new module on My App Name",
134
+ upstream=[
135
+ scaffold_my_app_name_module,
136
+ register_my_app_name_module,
137
+ register_my_app_name_module_config,
138
+ add_my_app_name_module_task_to_zrb_init,
139
+ ],
127
140
  retries=0,
128
141
  ),
129
142
  alias="module",
130
143
  )
131
- create_my_app_name_module << [
132
- scaffold_my_app_name_module,
133
- register_my_app_name_module,
134
- register_my_app_name_module_config,
135
- register_my_app_name_module_runner,
136
- ]
@@ -0,0 +1,7 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+
4
+ class AnyClient(ABC):
5
+ """
6
+ Defining client methods
7
+ """
@@ -0,0 +1,6 @@
1
+ from fastapp_template.config import APP_MY_MODULE_BASE_URL
2
+ from fastapp_template.module.module_template.client.any_client import AnyClient
3
+
4
+
5
+ class APIClient(AnyClient):
6
+ pass
@@ -1,6 +1,6 @@
1
- from fastapp_template.module.module_template.client.any_client import BaseClient
1
+ from fastapp_template.module.module_template.client.any_client import AnyClient
2
2
  from fastapp_template.module.module_template.service.user.usecase import user_usecase
3
3
 
4
4
 
5
- class DirectClient(BaseClient):
5
+ class DirectClient(AnyClient):
6
6
  pass
@@ -1,9 +1,9 @@
1
1
  from fastapp_template.config import APP_COMMUNICATION
2
- from fastapp_template.module.module_template.client.any_client import BaseClient
2
+ from fastapp_template.module.module_template.client.any_client import AnyClient
3
3
  from fastapp_template.module.module_template.client.api_client import APIClient
4
4
  from fastapp_template.module.module_template.client.direct_client import DirectClient
5
5
 
6
6
  if APP_COMMUNICATION == "direct":
7
- client: BaseClient = DirectClient()
7
+ client: AnyClient = DirectClient()
8
8
  elif APP_COMMUNICATION == "api":
9
- client: BaseClient = APIClient()
9
+ client: AnyClient = APIClient()
@@ -0,0 +1,33 @@
1
+ from fastapi import FastAPI
2
+ from fastapp_template.common.app import app
3
+ from fastapp_template.common.schema import BasicResponse
4
+ from fastapp_template.config import APP_MAIN_MODULE, APP_MODE, APP_MODULES
5
+
6
+
7
+ def serve_health_check(app: FastAPI):
8
+ @app.api_route("/health", methods=["GET", "HEAD"], response_model=BasicResponse)
9
+ async def health():
10
+ """
11
+ Microservice's health check
12
+ """
13
+ return BasicResponse(message="ok")
14
+
15
+
16
+ def serve_readiness_check(app: FastAPI):
17
+ @app.api_route("/readiness", methods=["GET", "HEAD"], response_model=BasicResponse)
18
+ async def readiness():
19
+ """
20
+ Microservice's readiness check
21
+ """
22
+ return BasicResponse(message="ok")
23
+
24
+
25
+ def serve_route(app: FastAPI):
26
+ if APP_MODE != "microservices" or "my_module" not in APP_MODULES:
27
+ return
28
+ if APP_MAIN_MODULE == "my_module":
29
+ serve_health_check(app)
30
+ serve_readiness_check(app)
31
+
32
+
33
+ serve_route(app)
@@ -2,18 +2,18 @@ import os
2
2
 
3
3
  from fastapp_template._zrb.column.create_column_task import create_my_app_name_column
4
4
  from fastapp_template._zrb.config import ACTIVATE_VENV_SCRIPT, APP_DIR
5
- from fastapp_template._zrb.entity.create_entity_task import create_my_app_name_entity
5
+ from fastapp_template._zrb.entity.task import create_my_app_name_entity
6
6
  from fastapp_template._zrb.group import (
7
7
  app_create_migration_group,
8
8
  app_migrate_group,
9
9
  app_run_group,
10
10
  )
11
- from fastapp_template._zrb.helper import (
11
+ from fastapp_template._zrb.module.task import create_my_app_name_module
12
+ from fastapp_template._zrb.util import (
12
13
  create_migration,
13
14
  migrate_module,
14
15
  run_microservice,
15
16
  )
16
- from fastapp_template._zrb.module.create_module_task import create_my_app_name_module
17
17
  from fastapp_template._zrb.venv_task import prepare_venv
18
18
 
19
19
  from zrb import CmdTask, Env, EnvFile, Task
@@ -67,6 +67,12 @@ class RouteParam:
67
67
  class BaseUsecase:
68
68
  _route_params: dict[str, RouteParam] = {}
69
69
 
70
+ def __init__(self):
71
+ self._route_params: dict[str, RouteParam] = {}
72
+ for name, method in self.__class__.__dict__.items():
73
+ if hasattr(method, "__route_param__"):
74
+ self._route_params[name] = getattr(method, "__route_param__")
75
+
70
76
  @classmethod
71
77
  def route(
72
78
  cls,
@@ -100,7 +106,15 @@ class BaseUsecase:
100
106
  """
101
107
 
102
108
  def decorator(func: Callable):
103
- cls._route_params[func.__name__] = RouteParam(
109
+ @wraps(func)
110
+ async def wrapped(*args, **kwargs):
111
+ return await func(*args, **kwargs)
112
+
113
+ # Inject __route_param__ property to the method
114
+ # Method with __route_param__ property will automatically
115
+ # registered to self._route_param and will be automatically exposed
116
+ # into DirectClient and APIClient
117
+ wrapped.__route_param__ = RouteParam(
104
118
  path=path,
105
119
  response_model=response_model,
106
120
  status_code=status_code,
@@ -126,11 +140,6 @@ class BaseUsecase:
126
140
  generate_unique_id_function=generate_unique_id_function,
127
141
  func=func,
128
142
  )
129
-
130
- @wraps(func)
131
- async def wrapped(*args, **kwargs):
132
- return await func(*args, **kwargs)
133
-
134
143
  return wrapped
135
144
 
136
145
  return decorator
@@ -144,6 +153,8 @@ class BaseUsecase:
144
153
  for name, details in _methods.items():
145
154
  func = details.func
146
155
  client_method = create_direct_client_method(func, self)
156
+ # Use __get__ to make a bounded method,
157
+ # ensuring that client_method use DirectClient as `self`
147
158
  setattr(DirectClient, name, client_method.__get__(DirectClient))
148
159
  return DirectClient
149
160
 
@@ -156,6 +167,8 @@ class BaseUsecase:
156
167
  # Dynamically generate methods
157
168
  for name, param in _methods.items():
158
169
  client_method = create_api_client_method(param, base_url)
170
+ # Use __get__ to make a bounded method,
171
+ # ensuring that client_method use APIClient as `self`
159
172
  setattr(APIClient, name, client_method.__get__(APIClient))
160
173
  return APIClient
161
174
 
@@ -8,6 +8,7 @@ APP_MODULES = [
8
8
  for module in os.getenv("MY_APP_NAME_MODULES", "").split(",")
9
9
  if module.strip() != ""
10
10
  ]
11
+ APP_MAIN_MODULE = APP_MODULES[0] if len(APP_MODULES) > 0 else None
11
12
  APP_PORT = int(os.getenv("MY_APP_NAME_PORT", "3000"))
12
13
  APP_COMMUNICATION = os.getenv(
13
14
  "MY_APP_NAME_COMMUNICATION", "direct" if APP_MODE == "monolith" else "api"
@@ -1,9 +1,13 @@
1
1
  from abc import ABC, abstractmethod
2
2
 
3
- from fastapp_template.schema.user import UserCreate, UserResponse, UserUpdate
3
+ from fastapp_template.schema.user import (
4
+ UserCreateWithAudit,
5
+ UserResponse,
6
+ UserUpdateWithAudit,
7
+ )
4
8
 
5
9
 
6
- class BaseClient(ABC):
10
+ class AnyClient(ABC):
7
11
  @abstractmethod
8
12
  async def get_user_by_id(self, user_id: str) -> UserResponse:
9
13
  pass
@@ -14,12 +18,14 @@ class BaseClient(ABC):
14
18
 
15
19
  @abstractmethod
16
20
  async def create_user(
17
- self, data: UserCreate | list[UserCreate]
21
+ self, data: UserCreateWithAudit | list[UserCreateWithAudit]
18
22
  ) -> UserResponse | list[UserResponse]:
19
23
  pass
20
24
 
21
25
  @abstractmethod
22
- async def update_user(self, user_id: str, data: UserUpdate) -> UserResponse:
26
+ async def update_user(
27
+ self, user_id: str, data: UserUpdateWithAudit
28
+ ) -> UserResponse:
23
29
  pass
24
30
 
25
31
  @abstractmethod
@@ -1,7 +1,7 @@
1
1
  from fastapp_template.config import APP_AUTH_BASE_URL
2
- from fastapp_template.module.auth.client.any_client import BaseClient
2
+ from fastapp_template.module.auth.client.any_client import AnyClient
3
3
  from fastapp_template.module.auth.service.user.user_usecase import user_usecase
4
4
 
5
5
 
6
- class APIClient(user_usecase.as_api_client(base_url=APP_AUTH_BASE_URL), BaseClient):
6
+ class APIClient(user_usecase.as_api_client(base_url=APP_AUTH_BASE_URL), AnyClient):
7
7
  pass
@@ -0,0 +1,6 @@
1
+ from fastapp_template.module.auth.client.any_client import AnyClient
2
+ from fastapp_template.module.auth.service.user.user_usecase import user_usecase
3
+
4
+
5
+ class DirectClient(user_usecase.as_direct_client(), AnyClient):
6
+ pass
@@ -1,9 +1,9 @@
1
1
  from fastapp_template.config import APP_COMMUNICATION
2
- from fastapp_template.module.auth.client.any_client import BaseClient
2
+ from fastapp_template.module.auth.client.any_client import AnyClient
3
3
  from fastapp_template.module.auth.client.api_client import APIClient
4
4
  from fastapp_template.module.auth.client.direct_client import DirectClient
5
5
 
6
6
  if APP_COMMUNICATION == "direct":
7
- client: BaseClient = DirectClient()
7
+ client: AnyClient = DirectClient()
8
8
  elif APP_COMMUNICATION == "api":
9
- client: BaseClient = APIClient()
9
+ client: AnyClient = APIClient()
@@ -0,0 +1,37 @@
1
+ from fastapi import FastAPI
2
+ from fastapp_template.common.app import app
3
+ from fastapp_template.common.schema import BasicResponse
4
+ from fastapp_template.config import APP_MAIN_MODULE, APP_MODE, APP_MODULES
5
+ from fastapp_template.module.auth.service.user.user_usecase import user_usecase
6
+
7
+
8
+ def serve_health_check(app: FastAPI):
9
+ @app.api_route("/health", methods=["GET", "HEAD"], response_model=BasicResponse)
10
+ async def health():
11
+ """
12
+ Microservice's health check
13
+ """
14
+ return BasicResponse(message="ok")
15
+
16
+
17
+ def serve_readiness_check(app: FastAPI):
18
+ @app.api_route("/readiness", methods=["GET", "HEAD"], response_model=BasicResponse)
19
+ async def readiness():
20
+ """
21
+ Microservice's readiness check
22
+ """
23
+ return BasicResponse(message="ok")
24
+
25
+
26
+ def serve_route(app: FastAPI):
27
+ if APP_MODE != "microservices" or "auth" not in APP_MODULES:
28
+ return
29
+ if APP_MAIN_MODULE == "auth":
30
+ serve_health_check(app)
31
+ serve_readiness_check(app)
32
+
33
+ # Serve user endpoints for APIClient
34
+ user_usecase.serve_route(app)
35
+
36
+
37
+ serve_route(app)
@@ -3,7 +3,12 @@ from fastapp_template.common.error import NotFoundError
3
3
  from fastapp_template.module.auth.service.user.repository.user_repository import (
4
4
  UserRepository,
5
5
  )
6
- from fastapp_template.schema.user import User, UserCreate, UserResponse, UserUpdate
6
+ from fastapp_template.schema.user import (
7
+ User,
8
+ UserCreateWithAudit,
9
+ UserResponse,
10
+ UserUpdateWithAudit,
11
+ )
7
12
  from passlib.context import CryptContext
8
13
  from sqlalchemy.ext.asyncio import AsyncSession
9
14
  from sqlmodel import Session, select
@@ -17,12 +22,13 @@ def hash_password(password: str) -> str:
17
22
 
18
23
 
19
24
  class UserDBRepository(
20
- BaseDBRepository[User, UserResponse, UserCreate, UserUpdate], UserRepository
25
+ BaseDBRepository[User, UserResponse, UserCreateWithAudit, UserUpdateWithAudit],
26
+ UserRepository,
21
27
  ):
22
28
  db_model = User
23
29
  response_model = UserResponse
24
- create_model = UserCreate
25
- update_model = UserUpdate
30
+ create_model = UserCreateWithAudit
31
+ update_model = UserUpdateWithAudit
26
32
  entity_name = "user"
27
33
  column_preprocessors = {"password": hash_password}
28
34
 
@@ -0,0 +1,43 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ from fastapp_template.schema.user import (
4
+ User,
5
+ UserCreateWithAudit,
6
+ UserResponse,
7
+ UserUpdateWithAudit,
8
+ )
9
+
10
+
11
+ class UserRepository(ABC):
12
+
13
+ @abstractmethod
14
+ async def create(self, user_data: UserCreateWithAudit) -> UserResponse:
15
+ pass
16
+
17
+ @abstractmethod
18
+ async def get_by_id(self, user_id: str) -> UserResponse:
19
+ pass
20
+
21
+ @abstractmethod
22
+ async def get_all(self) -> list[User]:
23
+ pass
24
+
25
+ @abstractmethod
26
+ async def update(
27
+ self, user_id: str, user_data: UserUpdateWithAudit
28
+ ) -> UserResponse:
29
+ pass
30
+
31
+ @abstractmethod
32
+ async def delete(self, user_id: str) -> UserResponse:
33
+ pass
34
+
35
+ @abstractmethod
36
+ async def create_bulk(
37
+ self, user_data_list: list[UserCreateWithAudit]
38
+ ) -> list[UserResponse]:
39
+ pass
40
+
41
+ @abstractmethod
42
+ async def get_by_credentials(self, username: str, password: str) -> UserResponse:
43
+ pass
@@ -3,7 +3,11 @@ from fastapp_template.module.auth.service.user.repository.factory import user_re
3
3
  from fastapp_template.module.auth.service.user.repository.user_repository import (
4
4
  UserRepository,
5
5
  )
6
- from fastapp_template.schema.user import UserCreate, UserResponse, UserUpdate
6
+ from fastapp_template.schema.user import (
7
+ UserCreateWithAudit,
8
+ UserResponse,
9
+ UserUpdateWithAudit,
10
+ )
7
11
 
8
12
 
9
13
  class UserUsecase(BaseUsecase):
@@ -30,16 +34,18 @@ class UserUsecase(BaseUsecase):
30
34
  response_model=UserResponse | list[UserResponse],
31
35
  )
32
36
  async def create_user(
33
- self, data: UserCreate | list[UserCreate]
37
+ self, data: UserCreateWithAudit | list[UserCreateWithAudit]
34
38
  ) -> UserResponse | list[UserResponse]:
35
- if isinstance(data, UserCreate):
39
+ if isinstance(data, UserCreateWithAudit):
36
40
  return await self.user_repository.create(data)
37
41
  return await self.user_repository.create_bulk(data)
38
42
 
39
43
  @BaseUsecase.route(
40
44
  "/api/v1/users/{user_id}", methods=["put"], response_model=UserResponse
41
45
  )
42
- async def update_user(self, user_id: str, data: UserUpdate) -> UserResponse:
46
+ async def update_user(
47
+ self, user_id: str, data: UserUpdateWithAudit
48
+ ) -> UserResponse:
43
49
  return await self.user_repository.update(user_id, data)
44
50
 
45
51
  @BaseUsecase.route(
@@ -0,0 +1,43 @@
1
+ from fastapi import FastAPI
2
+ from fastapp_template.common.app import app
3
+ from fastapp_template.common.schema import BasicResponse
4
+ from fastapp_template.config import APP_MAIN_MODULE, APP_MODE, APP_MODULES
5
+ from fastapp_template.module.auth.client.factory import client as auth_client
6
+ from fastapp_template.schema.user import UserCreate, UserResponse
7
+
8
+
9
+ def serve_health_check(app: FastAPI):
10
+ @app.api_route("/health", methods=["GET", "HEAD"], response_model=BasicResponse)
11
+ async def health():
12
+ """
13
+ My App Name's health check
14
+ """
15
+ return BasicResponse(message="ok")
16
+
17
+
18
+ def serve_readiness_check(app: FastAPI):
19
+ @app.api_route("/readiness", methods=["GET", "HEAD"], response_model=BasicResponse)
20
+ async def readiness():
21
+ """
22
+ My App Name's readiness check
23
+ """
24
+ return BasicResponse(message="ok")
25
+
26
+
27
+ def serve_route(app: FastAPI):
28
+ if APP_MODE != "monolith" and "gateway" not in APP_MODULES:
29
+ return
30
+ if APP_MODE == "monolith" or APP_MAIN_MODULE == "gateway":
31
+ serve_health_check(app)
32
+ serve_readiness_check(app)
33
+
34
+ @app.get("/api/v1/users", response_model=list[UserResponse])
35
+ async def auth_get_all_users() -> UserResponse:
36
+ return await auth_client.get_all_users()
37
+
38
+ @app.post("/api/v1/users", response_model=UserResponse | list[UserResponse])
39
+ async def auth_create_user(data: UserCreate | list[UserCreate]):
40
+ return await auth_client.create_user(data)
41
+
42
+
43
+ serve_route(app)
@@ -1,4 +1,4 @@
1
- fastapi[standard]~=0.115.5
1
+ fastapi[standard]~=0.115.6
2
2
  alembic~=1.14.0
3
3
  sqlmodel~=0.0.22
4
4
  ulid-py~=1.1.0
@@ -12,11 +12,19 @@ class PermissionCreate(PermissionBase):
12
12
  description: str
13
13
 
14
14
 
15
+ class PermissionCreateWithAudit(PermissionCreate):
16
+ created_by: str
17
+
18
+
15
19
  class PermissionUpdate(SQLModel):
16
20
  name: str | None = None
17
21
  description: str | None = None
18
22
 
19
23
 
24
+ class PermissionUpdateWithAudit(PermissionUpdate):
25
+ updated_by: str
26
+
27
+
20
28
  class PermissionResponse(PermissionBase):
21
29
  id: str
22
30
 
@@ -12,11 +12,19 @@ class RoleCreate(RoleBase):
12
12
  description: str
13
13
 
14
14
 
15
+ class RoleCreateWithAudit(RoleCreate):
16
+ created_by: str
17
+
18
+
15
19
  class RoleUpdate(SQLModel):
16
20
  name: str | None = None
17
21
  description: str | None = None
18
22
 
19
23
 
24
+ class RoleUpdateWithAudit(RoleUpdate):
25
+ updated_by: str
26
+
27
+
20
28
  class RoleResponse(RoleBase):
21
29
  id: str
22
30
 
@@ -12,11 +12,19 @@ class UserCreate(UserBase):
12
12
  password: str
13
13
 
14
14
 
15
+ class UserCreateWithAudit(UserCreate):
16
+ created_by: str
17
+
18
+
15
19
  class UserUpdate(SQLModel):
16
20
  username: str | None = None
17
21
  password: str | None = None
18
22
 
19
23
 
24
+ class UserUpdateWithAudit(UserUpdate):
25
+ updated_by: str
26
+
27
+
20
28
  class UserResponse(UserBase):
21
29
  id: str
22
30
 
@@ -0,0 +1,22 @@
1
+ from zrb.util.string.conversion import double_quote, to_snake_case
2
+
3
+
4
+ def get_zrb_init_import_code(old_code: str) -> str | None:
5
+ code = "from zrb import load_file"
6
+ if code in old_code:
7
+ return None
8
+ return code
9
+
10
+
11
+ def get_zrb_init_load_app_name_task(app: str) -> str:
12
+ snake_app_name = to_snake_case(app)
13
+ load_file_param = ", ".join(
14
+ [double_quote(part) for part in [snake_app_name, "_zrb", "task.py"]]
15
+ )
16
+ return "\n".join(
17
+ [
18
+ f"# Load {app} automation",
19
+ f"{snake_app_name} = load_file(os.path.join(_DIR, {load_file_param}))",
20
+ f"assert {snake_app_name}",
21
+ ]
22
+ )
@@ -21,7 +21,7 @@ scaffold_project = Scaffolder(
21
21
  name="project",
22
22
  description="Project name",
23
23
  prompt="Project name",
24
- default_str=lambda ctx: os.path.basename(ctx.input["project-dir"]),
24
+ default_str=lambda ctx: os.path.basename(ctx.input.project_dir),
25
25
  ),
26
26
  ],
27
27
  source_path=os.path.join(_DIR, "project-template"),