zrb 0.23.4__py3-none-any.whl → 0.24.0__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 (98) hide show
  1. zrb/__init__.py +2 -0
  2. zrb/builtin/devtool/install/_input.py +2 -2
  3. zrb/builtin/project/add/app/generator/template/src/kebab-zrb-package-name/src/snake_zrb_package_name/snake_zrb_generator_name/template/_automate/snake_zrb_app_name/container/start.py +5 -6
  4. zrb/builtin/project/add/app/python/template/_automate/snake_zrb_app_name/container/start.py +5 -6
  5. zrb/builtin/project/add/fastapp/app/template/_automate/snake_zrb_app_name/container/_helper.py +1 -52
  6. zrb/builtin/project/add/fastapp/app/template/_automate/snake_zrb_app_name/container/microservices/start.py +5 -6
  7. zrb/builtin/project/add/fastapp/app/template/_automate/snake_zrb_app_name/container/monolith/start.py +5 -6
  8. zrb/builtin/project/add/fastapp/app/template/_automate/snake_zrb_app_name/container/support/start.py +4 -6
  9. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/loadtest/locustfile.py +1 -3
  10. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/loadtest/template.env +1 -1
  11. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/src/config.py +59 -61
  12. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/src/integration/app/app.py +28 -28
  13. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/src/integration/app/app_lifespan.py +15 -15
  14. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/src/integration/app/app_state.py +2 -2
  15. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/src/integration/db_connection.py +14 -2
  16. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/src/integration/frontend_index.py +2 -2
  17. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/src/integration/log.py +6 -6
  18. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/src/integration/messagebus.py +33 -33
  19. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/src/integration/rpc.py +9 -9
  20. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/src/module/auth/integration/access_token_scheme.py +2 -2
  21. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/src/module/auth/integration/access_token_util.py +7 -7
  22. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/src/module/auth/integration/model/user_model.py +6 -6
  23. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/src/module/auth/integration/refresh_token_util.py +7 -7
  24. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/src/module/auth/integration/user.py +18 -18
  25. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/src/module/auth/migrate.py +2 -2
  26. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/src/module/auth/register_module.py +8 -8
  27. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/src/module/log/migrate.py +2 -2
  28. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/src/module/log/register_module.py +8 -8
  29. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/src/template.env +5 -2
  30. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/test/auth/test_group_crud.py +7 -7
  31. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/test/auth/test_permission_crud.py +7 -7
  32. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/test/auth/test_user_crud.py +7 -7
  33. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/test/auth/test_user_login.py +14 -14
  34. zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/test/test_liveness_and_readiness.py +3 -3
  35. zrb/builtin/project/add/fastapp/crud/_helper/__init__.py +7 -0
  36. zrb/builtin/project/add/fastapp/crud/_helper/_common.py +8 -0
  37. zrb/builtin/project/add/fastapp/crud/_helper/register_api.py +45 -0
  38. zrb/builtin/project/add/fastapp/crud/_helper/register_permission.py +40 -0
  39. zrb/builtin/project/add/fastapp/crud/_helper/register_rpc.py +45 -0
  40. zrb/builtin/project/add/fastapp/crud/crud.py +3 -24
  41. zrb/builtin/project/add/fastapp/crud/template/src/kebab-zrb-app-name/src/module/snake_zrb_module_name/entity/snake_zrb_entity_name/repo.py +12 -1
  42. zrb/builtin/project/add/fastapp/crud/template/src/kebab-zrb-app-name/src/module/snake_zrb_module_name/schema/snake_zrb_entity_name.py +1 -0
  43. zrb/builtin/project/add/fastapp/crud/template/src/kebab-zrb-app-name/test/snake_zrb_module_name/test_snake_zrb_entity_name.py +8 -7
  44. zrb/builtin/project/add/fastapp/field/_helper/__init__.py +17 -0
  45. zrb/builtin/project/add/fastapp/field/_helper/_common.py +102 -0
  46. zrb/builtin/project/add/fastapp/field/_helper/inject_delete_page.py +49 -0
  47. zrb/builtin/project/add/fastapp/field/_helper/inject_detail_page.py +49 -0
  48. zrb/builtin/project/add/fastapp/field/_helper/inject_insert_page.py +62 -0
  49. zrb/builtin/project/add/fastapp/field/_helper/inject_list_page.py +47 -0
  50. zrb/builtin/project/add/fastapp/field/_helper/inject_repo.py +47 -0
  51. zrb/builtin/project/add/fastapp/field/_helper/inject_schema.py +45 -0
  52. zrb/builtin/project/add/fastapp/field/_helper/inject_test.py +49 -0
  53. zrb/builtin/project/add/fastapp/field/_helper/inject_update_page.py +50 -0
  54. zrb/builtin/project/add/fastapp/field/_input.py +14 -2
  55. zrb/builtin/project/add/fastapp/field/field.py +51 -69
  56. zrb/builtin/project/add/fastapp/module/_helper/__init__.py +17 -0
  57. zrb/builtin/project/add/fastapp/module/_helper/append_all_disabled_env.py +22 -0
  58. zrb/builtin/project/add/fastapp/module/_helper/append_all_enabled_env.py +22 -0
  59. zrb/builtin/project/add/fastapp/module/_helper/append_deployment_template_env.py +25 -0
  60. zrb/builtin/project/add/fastapp/module/_helper/append_src_template_env.py +25 -0
  61. zrb/builtin/project/add/fastapp/module/_helper/create_app_config.py +29 -0
  62. zrb/builtin/project/add/fastapp/module/_helper/create_microservice_config.py +158 -0
  63. zrb/builtin/project/add/fastapp/module/_helper/register_migration.py +35 -0
  64. zrb/builtin/project/add/fastapp/module/_helper/register_module.py +33 -0
  65. zrb/builtin/project/add/fastapp/module/module.py +8 -37
  66. zrb/builtin/project/add/fastapp/module/template/src/kebab-zrb-app-name/src/module/snake_zrb_module_name/migrate.py +2 -2
  67. zrb/builtin/project/add/fastapp/module/template/src/kebab-zrb-app-name/src/module/snake_zrb_module_name/register_module.py +8 -8
  68. zrb/builtin/project/add/plugin/plugin.py +2 -2
  69. zrb/builtin/project/create/create.py +2 -2
  70. zrb/builtin/version.py +3 -3
  71. zrb/config/config.py +14 -14
  72. zrb/helper/callable.py +3 -1
  73. zrb/helper/cli.py +4 -4
  74. zrb/helper/log.py +3 -3
  75. zrb/helper/typecheck.py +2 -2
  76. zrb/helper/typing.py +2 -2
  77. zrb/helper/util.py +12 -0
  78. zrb/runner.py +2 -2
  79. zrb/task/base_task/base_task.py +3 -3
  80. zrb/task/base_task/component/base_task_model.py +9 -9
  81. zrb/task/cmd_task.py +4 -4
  82. zrb/task/docker_compose_start_task.py +151 -0
  83. zrb/task/docker_compose_task.py +32 -15
  84. zrb/task_input/base_input.py +2 -2
  85. zrb/task_input/multiline_input.py +2 -2
  86. {zrb-0.23.4.dist-info → zrb-0.24.0.dist-info}/METADATA +2 -2
  87. {zrb-0.23.4.dist-info → zrb-0.24.0.dist-info}/RECORD +90 -73
  88. zrb/builtin/project/add/app/generator/template/src/kebab-zrb-package-name/src/snake_zrb_package_name/snake_zrb_generator_name/template/_automate/snake_zrb_app_name/container/init.py +0 -34
  89. zrb/builtin/project/add/app/python/template/_automate/snake_zrb_app_name/container/init.py +0 -34
  90. zrb/builtin/project/add/fastapp/app/template/_automate/snake_zrb_app_name/container/microservices/init.py +0 -36
  91. zrb/builtin/project/add/fastapp/app/template/_automate/snake_zrb_app_name/container/monolith/init.py +0 -36
  92. zrb/builtin/project/add/fastapp/app/template/_automate/snake_zrb_app_name/container/support/init.py +0 -26
  93. zrb/builtin/project/add/fastapp/crud/_helper.py +0 -118
  94. zrb/builtin/project/add/fastapp/field/_helper.py +0 -328
  95. zrb/builtin/project/add/fastapp/module/_helper.py +0 -313
  96. {zrb-0.23.4.dist-info → zrb-0.24.0.dist-info}/LICENSE +0 -0
  97. {zrb-0.23.4.dist-info → zrb-0.24.0.dist-info}/WHEEL +0 -0
  98. {zrb-0.23.4.dist-info → zrb-0.24.0.dist-info}/entry_points.txt +0 -0
@@ -2,7 +2,7 @@ from typing import AsyncIterator
2
2
 
3
3
  import pytest
4
4
  from httpx import AsyncClient
5
- from src.config import app_auth_admin_password, app_auth_admin_username
5
+ from src.config import APP_AUTH_ADMIN_PASSWORD, APP_AUTH_ADMIN_USERNAME
6
6
 
7
7
 
8
8
  @pytest.mark.asyncio
@@ -13,8 +13,8 @@ async def test_admin_user_login_success(
13
13
  login_response = await client.post(
14
14
  "/api/v1/auth/login",
15
15
  json={
16
- "identity": app_auth_admin_username,
17
- "password": app_auth_admin_password,
16
+ "identity": APP_AUTH_ADMIN_USERNAME,
17
+ "password": APP_AUTH_ADMIN_PASSWORD,
18
18
  },
19
19
  )
20
20
  assert login_response.status_code == 200
@@ -30,7 +30,7 @@ async def test_admin_user_login_empty_identity_failed(
30
30
  async for client in test_client_generator:
31
31
  login_response = await client.post(
32
32
  "/api/v1/auth/login",
33
- json={"identity": "", "password": app_auth_admin_password},
33
+ json={"identity": "", "password": APP_AUTH_ADMIN_PASSWORD},
34
34
  )
35
35
  assert login_response.status_code == 422
36
36
 
@@ -42,7 +42,7 @@ async def test_admin_user_login_invalid_identity_failed(
42
42
  async for client in test_client_generator:
43
43
  login_response = await client.post(
44
44
  "/api/v1/auth/login",
45
- json={"identity": "invalid-identity", "password": app_auth_admin_password},
45
+ json={"identity": "invalid-identity", "password": APP_AUTH_ADMIN_PASSWORD},
46
46
  )
47
47
  assert login_response.status_code == 404
48
48
 
@@ -54,7 +54,7 @@ async def test_admin_user_failed_invalid_password(
54
54
  async for client in test_client_generator:
55
55
  login_response = await client.post(
56
56
  "/api/v1/auth/login",
57
- json={"identity": app_auth_admin_username, "password": "invalid-password"},
57
+ json={"identity": APP_AUTH_ADMIN_USERNAME, "password": "invalid-password"},
58
58
  )
59
59
  assert login_response.status_code == 404
60
60
 
@@ -68,8 +68,8 @@ async def test_create_normal_user_and_login_with_username_success(
68
68
  login_admin_response = await client.post(
69
69
  "/api/v1/auth/login",
70
70
  json={
71
- "identity": app_auth_admin_username,
72
- "password": app_auth_admin_password,
71
+ "identity": APP_AUTH_ADMIN_USERNAME,
72
+ "password": APP_AUTH_ADMIN_PASSWORD,
73
73
  },
74
74
  )
75
75
  assert login_admin_response.status_code == 200
@@ -113,8 +113,8 @@ async def test_create_normal_user_and_login_with_phone_success(
113
113
  login_admin_response = await client.post(
114
114
  "/api/v1/auth/login",
115
115
  json={
116
- "identity": app_auth_admin_username,
117
- "password": app_auth_admin_password,
116
+ "identity": APP_AUTH_ADMIN_USERNAME,
117
+ "password": APP_AUTH_ADMIN_PASSWORD,
118
118
  },
119
119
  )
120
120
  assert login_admin_response.status_code == 200
@@ -155,8 +155,8 @@ async def test_create_normal_user_and_login_with_email_success(
155
155
  login_admin_response = await client.post(
156
156
  "/api/v1/auth/login",
157
157
  json={
158
- "identity": app_auth_admin_username,
159
- "password": app_auth_admin_password,
158
+ "identity": APP_AUTH_ADMIN_USERNAME,
159
+ "password": APP_AUTH_ADMIN_PASSWORD,
160
160
  },
161
161
  )
162
162
  assert login_admin_response.status_code == 200
@@ -200,8 +200,8 @@ async def test_create_normal_user_and_login_failed(
200
200
  login_admin_response = await client.post(
201
201
  "/api/v1/auth/login",
202
202
  json={
203
- "identity": app_auth_admin_username,
204
- "password": app_auth_admin_password,
203
+ "identity": APP_AUTH_ADMIN_USERNAME,
204
+ "password": APP_AUTH_ADMIN_PASSWORD,
205
205
  },
206
206
  )
207
207
  assert login_admin_response.status_code == 200
@@ -2,7 +2,7 @@ from typing import AsyncIterator
2
2
 
3
3
  import pytest
4
4
  from httpx import AsyncClient
5
- from src.config import zrb_app_name
5
+ from src.config import APP_NAME
6
6
 
7
7
 
8
8
  @pytest.mark.asyncio
@@ -10,7 +10,7 @@ async def test_get_liveness(test_client_generator: AsyncIterator[AsyncClient]):
10
10
  async for client in test_client_generator:
11
11
  response = await client.get("/liveness")
12
12
  assert response.status_code == 200
13
- assert response.json() == {"app": zrb_app_name, "alive": True}
13
+ assert response.json() == {"app": APP_NAME, "alive": True}
14
14
 
15
15
 
16
16
  @pytest.mark.asyncio
@@ -25,7 +25,7 @@ async def test_get_readiness(test_client_generator: AsyncIterator[AsyncClient]):
25
25
  async for client in test_client_generator:
26
26
  response = await client.get("/readiness")
27
27
  assert response.status_code == 200
28
- assert response.json() == {"app": zrb_app_name, "ready": True}
28
+ assert response.json() == {"app": APP_NAME, "ready": True}
29
29
 
30
30
 
31
31
  @pytest.mark.asyncio
@@ -0,0 +1,7 @@
1
+ from .register_api import register_api
2
+ from .register_permission import register_permission
3
+ from .register_rpc import register_rpc
4
+
5
+ assert register_api
6
+ assert register_permission
7
+ assert register_rpc
@@ -0,0 +1,8 @@
1
+ import os
2
+
3
+ from zrb.helper.util import to_kebab_case
4
+
5
+
6
+ def get_app_module_dir(project_dir: str, app_name: str) -> str:
7
+ kebab_app_name = to_kebab_case(app_name)
8
+ return os.path.join(project_dir, "src", kebab_app_name, "src", "module")
@@ -0,0 +1,45 @@
1
+ import os
2
+
3
+ from zrb.helper.codemod.add_import_module import add_import_module
4
+ from zrb.helper.codemod.append_code_to_function import append_code_to_function
5
+ from zrb.helper.file.text import read_text_file_async, write_text_file_async
6
+ from zrb.helper.typecheck import typechecked
7
+ from zrb.helper.util import to_snake_case
8
+ from zrb.task.task import Task
9
+
10
+ from ._common import get_app_module_dir
11
+
12
+
13
+ @typechecked
14
+ async def register_api(
15
+ task: Task, project_dir: str, app_name: str, module_name: str, entity_name: str
16
+ ):
17
+ snake_module_name = to_snake_case(module_name)
18
+ snake_entity_name = to_snake_case(entity_name)
19
+ module_api_file_path = os.path.join(
20
+ get_app_module_dir(project_dir, app_name), snake_module_name, "api.py"
21
+ )
22
+ register_function_path = ".".join(
23
+ ["module", snake_module_name, "entity", snake_entity_name, "api"]
24
+ )
25
+ register_function = f"register_{snake_entity_name}_api"
26
+ task.print_out(f"Read code from: {module_api_file_path}")
27
+ code = await read_text_file_async(module_api_file_path)
28
+ task.print_out(
29
+ f'Add import "register_api" as "{register_function}" '
30
+ + f'from "{register_function_path}" to the code'
31
+ )
32
+ code = add_import_module(
33
+ code=code,
34
+ module_path=register_function_path,
35
+ resource="register_api",
36
+ alias=register_function,
37
+ )
38
+ task.print_out(f'Add "{register_function}" call to the code')
39
+ code = append_code_to_function(
40
+ code=code,
41
+ function_name="register_api",
42
+ new_code=f"{register_function}(logger, app, authorizer, rpc_caller, publisher)", # noqa
43
+ )
44
+ task.print_out(f"Write modified code to: {module_api_file_path}")
45
+ await write_text_file_async(module_api_file_path, code)
@@ -0,0 +1,40 @@
1
+ import os
2
+
3
+ from zrb.helper.codemod.append_code_to_function import append_code_to_function
4
+ from zrb.helper.file.text import read_text_file_async, write_text_file_async
5
+ from zrb.helper.typecheck import typechecked
6
+ from zrb.helper.util import to_snake_case
7
+ from zrb.task.task import Task
8
+
9
+ from ._common import get_app_module_dir
10
+
11
+
12
+ @typechecked
13
+ async def register_permission(
14
+ task: Task, project_dir: str, app_name: str, module_name: str, entity_name: str
15
+ ):
16
+ snake_module_name = to_snake_case(module_name)
17
+ snake_entity_name = to_snake_case(entity_name)
18
+ module_register_permission_file_path = os.path.join(
19
+ get_app_module_dir(project_dir, app_name),
20
+ "auth",
21
+ "register_permission.py",
22
+ )
23
+ task.print_out(f"Read code from: {module_register_permission_file_path}")
24
+ code = await read_text_file_async(module_register_permission_file_path)
25
+ code = append_code_to_function(
26
+ code=code,
27
+ function_name="register_permission",
28
+ new_code="\n".join(
29
+ [
30
+ "await ensure_entity_permission(",
31
+ f" module_name='{snake_module_name}', entity_name='{snake_entity_name}'", # noqa
32
+ ")",
33
+ ]
34
+ ),
35
+ )
36
+ task.print_out(
37
+ f'Add "ensure_entity_permission" call for {snake_entity_name} ' + "to the code"
38
+ )
39
+ task.print_out(f"Write modified code to: {module_register_permission_file_path}")
40
+ await write_text_file_async(module_register_permission_file_path, code)
@@ -0,0 +1,45 @@
1
+ import os
2
+
3
+ from zrb.helper.codemod.add_import_module import add_import_module
4
+ from zrb.helper.codemod.append_code_to_function import append_code_to_function
5
+ from zrb.helper.file.text import read_text_file_async, write_text_file_async
6
+ from zrb.helper.typecheck import typechecked
7
+ from zrb.helper.util import to_snake_case
8
+ from zrb.task.task import Task
9
+
10
+ from ._common import get_app_module_dir
11
+
12
+
13
+ @typechecked
14
+ async def register_rpc(
15
+ task: Task, project_dir: str, app_name: str, module_name: str, entity_name: str
16
+ ):
17
+ snake_module_name = to_snake_case(module_name)
18
+ snake_entity_name = to_snake_case(entity_name)
19
+ module_rpc_file_path = os.path.join(
20
+ get_app_module_dir(project_dir, app_name), snake_module_name, "rpc.py"
21
+ )
22
+ register_function_path = ".".join(
23
+ ["module", snake_module_name, "entity", snake_entity_name, "rpc"]
24
+ )
25
+ register_function = f"register_{snake_entity_name}_rpc"
26
+ task.print_out(f"Read code from: {module_rpc_file_path}")
27
+ code = await read_text_file_async(module_rpc_file_path)
28
+ task.print_out(
29
+ f'Add import "register_rpc" as "{register_function}" '
30
+ + f'from "{register_function_path}" to the code'
31
+ )
32
+ code = add_import_module(
33
+ code=code,
34
+ module_path=register_function_path,
35
+ resource="register_rpc",
36
+ alias=register_function,
37
+ )
38
+ task.print_out(f'Add "{register_function}" call to the code')
39
+ code = append_code_to_function(
40
+ code=code,
41
+ function_name="register_rpc",
42
+ new_code=f"{register_function}(logger, rpc_server, rpc_caller, publisher)", # noqa
43
+ )
44
+ task.print_out(f"Write modified code to: {module_rpc_file_path}")
45
+ await write_text_file_async(module_rpc_file_path, code)
@@ -107,36 +107,15 @@ async def register_crud(*args: Any, **kwargs: Any):
107
107
  app_name = kwargs.get("app_name")
108
108
  module_name = kwargs.get("module_name")
109
109
  entity_name = kwargs.get("entity_name")
110
- kebab_app_name = util.to_kebab_case(app_name)
111
- snake_module_name = util.to_snake_case(module_name)
112
- snake_entity_name = util.to_snake_case(entity_name)
113
110
  await asyncio.gather(
114
111
  asyncio.create_task(
115
- register_api(
116
- task=task,
117
- project_dir=project_dir,
118
- kebab_app_name=kebab_app_name,
119
- snake_module_name=snake_module_name,
120
- snake_entity_name=snake_entity_name,
121
- )
112
+ register_api(task, project_dir, app_name, module_name, entity_name)
122
113
  ),
123
114
  asyncio.create_task(
124
- register_rpc(
125
- task=task,
126
- project_dir=project_dir,
127
- kebab_app_name=kebab_app_name,
128
- snake_module_name=snake_module_name,
129
- snake_entity_name=snake_entity_name,
130
- )
115
+ register_rpc(task, project_dir, app_name, module_name, entity_name)
131
116
  ),
132
117
  asyncio.create_task(
133
- register_permission(
134
- task=task,
135
- project_dir=project_dir,
136
- kebab_app_name=kebab_app_name,
137
- snake_module_name=snake_module_name,
138
- snake_entity_name=snake_entity_name,
139
- )
118
+ register_permission(task, project_dir, app_name, module_name, entity_name)
140
119
  ),
141
120
  )
142
121
 
@@ -4,7 +4,18 @@ from module.snake_zrb_module_name.schema.snake_zrb_entity_name import (
4
4
  PascalZrbEntityName,
5
5
  PascalZrbEntityNameData,
6
6
  )
7
- from sqlalchemy import Column, String
7
+ from sqlalchemy import (
8
+ Boolean,
9
+ Column,
10
+ Date,
11
+ DateTime,
12
+ Double,
13
+ Float,
14
+ Integer,
15
+ String,
16
+ Text,
17
+ Time,
18
+ )
8
19
 
9
20
 
10
21
  class DBEntityPascalZrbEntityName(Base, DBEntityMixin):
@@ -1,3 +1,4 @@
1
+ from datetime import date, datetime, time
1
2
  from typing import List, Optional
2
3
 
3
4
  from component.schema import BaseCountSchema, BaseDateTimeSchema
@@ -1,8 +1,9 @@
1
+ from datetime import date, datetime, time
1
2
  from typing import AsyncIterator
2
3
 
3
4
  import pytest
5
+ from config import APP_AUTH_ADMIN_PASSWORD, APP_AUTH_ADMIN_USERNAME
4
6
  from httpx import AsyncClient
5
- from src.config import app_auth_admin_password, app_auth_admin_username
6
7
 
7
8
  inserted_success_data = {
8
9
  "snake_zrb_column_name": "test-kebab-create-entity-name-success"
@@ -27,8 +28,8 @@ async def test_insert_snake_zrb_entity_name_and_get_success(
27
28
  login_admin_response = await client.post(
28
29
  "/api/v1/auth/login",
29
30
  json={
30
- "identity": app_auth_admin_username,
31
- "password": app_auth_admin_password,
31
+ "identity": APP_AUTH_ADMIN_USERNAME,
32
+ "password": APP_AUTH_ADMIN_PASSWORD,
32
33
  },
33
34
  )
34
35
  assert login_admin_response.status_code == 200
@@ -80,8 +81,8 @@ async def test_update_snake_zrb_entity_name_and_get_success(
80
81
  login_admin_response = await client.post(
81
82
  "/api/v1/auth/login",
82
83
  json={
83
- "identity": app_auth_admin_username,
84
- "password": app_auth_admin_password,
84
+ "identity": APP_AUTH_ADMIN_USERNAME,
85
+ "password": APP_AUTH_ADMIN_PASSWORD,
85
86
  },
86
87
  )
87
88
  assert login_admin_response.status_code == 200
@@ -147,8 +148,8 @@ async def test_delete_snake_zrb_entity_name_and_get_success(
147
148
  login_admin_response = await client.post(
148
149
  "/api/v1/auth/login",
149
150
  json={
150
- "identity": app_auth_admin_username,
151
- "password": app_auth_admin_password,
151
+ "identity": APP_AUTH_ADMIN_USERNAME,
152
+ "password": APP_AUTH_ADMIN_PASSWORD,
152
153
  },
153
154
  )
154
155
  assert login_admin_response.status_code == 200
@@ -0,0 +1,17 @@
1
+ from .inject_delete_page import inject_delete_page
2
+ from .inject_detail_page import inject_detail_page
3
+ from .inject_insert_page import inject_insert_page
4
+ from .inject_list_page import inject_list_page
5
+ from .inject_repo import inject_repo
6
+ from .inject_schema import inject_schema
7
+ from .inject_test import inject_test
8
+ from .inject_update_page import inject_update_page
9
+
10
+ assert inject_list_page
11
+ assert inject_detail_page
12
+ assert inject_insert_page
13
+ assert inject_update_page
14
+ assert inject_delete_page
15
+ assert inject_repo
16
+ assert inject_schema
17
+ assert inject_test
@@ -0,0 +1,102 @@
1
+ import os
2
+
3
+ from zrb.helper.string.parse_replacement import parse_replacement
4
+ from zrb.helper.util import to_kebab_case, to_snake_case
5
+
6
+
7
+ def replace_marker(text: str, marker: str, code: str) -> str:
8
+ return parse_replacement(text, {marker: "\n".join([code, marker])})
9
+
10
+
11
+ def get_app_frontend_routes_dir(project_dir: str, app_name: str) -> str:
12
+ return os.path.join(
13
+ get_app_dir(project_dir, app_name), "src", "frontend", "src", "routes"
14
+ )
15
+
16
+
17
+ def get_app_dir(project_dir: str, app_name: str) -> str:
18
+ kebab_app_name = to_kebab_case(app_name)
19
+ return os.path.join(project_dir, "src", kebab_app_name)
20
+
21
+
22
+ def get_sqlalchemy_column_type(column_type: str) -> str:
23
+ default_value = "String"
24
+ type_map = {
25
+ "string": default_value,
26
+ "text": "Text",
27
+ "boolean": "Boolean",
28
+ "integer": "Integer",
29
+ "float": "Float",
30
+ "double": "Double",
31
+ "date": "Date",
32
+ "datetime": "DateTime",
33
+ "time": "Time",
34
+ }
35
+ column_type = column_type.lower()
36
+ return type_map.get(column_type.lower(), default_value)
37
+
38
+
39
+ def get_python_column_type(column_type: str) -> str:
40
+ default_value = "str"
41
+ type_map = {
42
+ "string": default_value,
43
+ "text": "str",
44
+ "boolean": "bool",
45
+ "integer": "int",
46
+ "float": "float",
47
+ "double": "double",
48
+ "date": "date",
49
+ "datetime": "datetime",
50
+ "time": "time",
51
+ }
52
+ return type_map.get(column_type.lower(), default_value)
53
+
54
+
55
+ def get_python_value_for_testing(column_type: str) -> str:
56
+ default_value = '"A string"'
57
+ type_map = {
58
+ "string": default_value,
59
+ "text": '"A text"',
60
+ "boolean": "True",
61
+ "integer": "42",
62
+ "float": "3.14",
63
+ "double": "3.14",
64
+ "date": "date(2024, 8, 10)",
65
+ "datetime": "datetime(2024, 8, 10, 14, 30)",
66
+ "time": "time(14, 30)",
67
+ }
68
+ return type_map.get(column_type.lower(), default_value)
69
+
70
+
71
+ def get_default_js_value(column_type: str) -> str:
72
+ default_value = '""'
73
+ type_map = {
74
+ "string": default_value,
75
+ "text": '""',
76
+ "boolean": "true",
77
+ "integer": "0",
78
+ "float": "0.0",
79
+ "double": "0.0",
80
+ "date": "new Date()",
81
+ "datetime": "new Date()",
82
+ "time": "new Date().toLocaleTimeString('en-US', { hour12: false })",
83
+ }
84
+ return type_map.get(column_type.lower(), default_value)
85
+
86
+
87
+ def get_html_input(column_type: str, column_name: str, column_caption: str) -> str:
88
+ snake_column_name = to_snake_case(column_name)
89
+ kebab_column_name = to_kebab_case(column_name)
90
+ default_value = f'<input type="text" class="input w-full" id="{kebab_column_name}" placeholder="{column_caption}" bind:value="{{row.{snake_column_name}}}" />' # noqa
91
+ type_map = {
92
+ "string": default_value,
93
+ "text": f'<textarea class="textarea w-full" id="{kebab_column_name}" placeholder="{column_caption}" bind:value="{{row.{snake_column_name}}}"></textarea>', # noqa
94
+ "boolean": f'<input type="checkbox" class="checkbox" id="{kebab_column_name}" bind:checked="{{row.{snake_column_name}}}" />', # noqa
95
+ "integer": f'<input type="number" class="input w-full" id="{kebab_column_name}" placeholder="{column_caption}" bind:value="{{row.{snake_column_name}}}" />', # noqa
96
+ "float": f'<input type="number" step="0.01" class="input w-full" id="{kebab_column_name}" placeholder="{column_caption}" bind:value="{{row.{snake_column_name}}}" />', # noqa
97
+ "double": f'<input type="number" step="0.01" class="input w-full" id="{kebab_column_name}" placeholder="{column_caption}" bind:value="{{row.{snake_column_name}}}" />', # noqa
98
+ "date": f'<input type="date" class="input w-full" id="{kebab_column_name}" placeholder="{column_caption}" bind:value="{{row.{snake_column_name}}}" />', # noqa
99
+ "datetime": f'<input type="datetime-local" class="input w-full" id="{kebab_column_name}" placeholder="{column_caption}" bind:value="{{row.{snake_column_name}}}" />', # noqa
100
+ "time": f'<input type="time" class="input w-full" id="{kebab_column_name}" placeholder="{column_caption}" bind:value="{{row.{snake_column_name}}}" />', # noqa
101
+ }
102
+ return type_map.get(column_type.lower(), default_value)
@@ -0,0 +1,49 @@
1
+ import os
2
+
3
+ from zrb.helper.file.text import read_text_file_async, write_text_file_async
4
+ from zrb.helper.typecheck import typechecked
5
+ from zrb.helper.util import to_capitalized_human_readable, to_kebab_case, to_snake_case
6
+ from zrb.task.task import Task
7
+
8
+ from ._common import get_app_frontend_routes_dir, replace_marker
9
+
10
+
11
+ @typechecked
12
+ async def inject_delete_page(
13
+ task: Task,
14
+ project_dir: str,
15
+ app_name: str,
16
+ module_name: str,
17
+ entity_name: str,
18
+ column_name: str,
19
+ ):
20
+ kebab_module_name = to_kebab_case(module_name)
21
+ kebab_entity_name = to_kebab_case(entity_name)
22
+ snake_column_name = to_snake_case(column_name)
23
+ kebab_column_name = to_kebab_case(column_name)
24
+ column_caption = to_capitalized_human_readable(column_name)
25
+ file_path = os.path.join(
26
+ get_app_frontend_routes_dir(project_dir, app_name),
27
+ kebab_module_name,
28
+ kebab_entity_name,
29
+ "delete",
30
+ "[id]",
31
+ "+page.svelte",
32
+ )
33
+ task.print_out(f"Read HTML from: {file_path}")
34
+ html_content = await read_text_file_async(file_path)
35
+ task.print_out("Add field to delete page")
36
+ html_content = replace_marker(
37
+ html_content,
38
+ marker="<!-- DON'T DELETE: insert new field here-->",
39
+ code="\n".join(
40
+ [
41
+ '<div class="mb-4">',
42
+ f' <label class="block text-gray-700 font-bold mb-2" for="{kebab_column_name}">{column_caption}</label>', # noqa
43
+ f' <span id="{kebab_column_name}">{{row.{snake_column_name}}}</span>',
44
+ "</div>",
45
+ ]
46
+ ),
47
+ )
48
+ task.print_out(f"Write modified HTML to: {file_path}")
49
+ await write_text_file_async(file_path, html_content)
@@ -0,0 +1,49 @@
1
+ import os
2
+
3
+ from zrb.helper.file.text import read_text_file_async, write_text_file_async
4
+ from zrb.helper.typecheck import typechecked
5
+ from zrb.helper.util import to_capitalized_human_readable, to_kebab_case, to_snake_case
6
+ from zrb.task.task import Task
7
+
8
+ from ._common import get_app_frontend_routes_dir, replace_marker
9
+
10
+
11
+ @typechecked
12
+ async def inject_detail_page(
13
+ task: Task,
14
+ project_dir: str,
15
+ app_name: str,
16
+ module_name: str,
17
+ entity_name: str,
18
+ column_name: str,
19
+ ):
20
+ kebab_module_name = to_kebab_case(module_name)
21
+ kebab_entity_name = to_kebab_case(entity_name)
22
+ snake_column_name = to_snake_case(column_name)
23
+ kebab_column_name = to_kebab_case(column_name)
24
+ column_caption = to_capitalized_human_readable(column_name)
25
+ file_path = os.path.join(
26
+ get_app_frontend_routes_dir(project_dir, app_name),
27
+ kebab_module_name,
28
+ kebab_entity_name,
29
+ "detail",
30
+ "[id]",
31
+ "+page.svelte",
32
+ )
33
+ task.print_out(f"Read HTML from: {file_path}")
34
+ html_content = await read_text_file_async(file_path)
35
+ task.print_out("Add field to detail page")
36
+ html_content = replace_marker(
37
+ html_content,
38
+ marker="<!-- DON'T DELETE: insert new field here-->",
39
+ code="\n".join(
40
+ [
41
+ '<div class="mb-4">',
42
+ f' <label class="block text-gray-700 font-bold mb-2" for="{kebab_column_name}">{column_caption}</label>', # noqa
43
+ f' <span id="{kebab_column_name}">{{row.{snake_column_name}}}</span>',
44
+ "</div>",
45
+ ]
46
+ ),
47
+ )
48
+ task.print_out(f"Write modified HTML to: {file_path}")
49
+ await write_text_file_async(file_path, html_content)
@@ -0,0 +1,62 @@
1
+ import os
2
+
3
+ from zrb.helper.file.text import read_text_file_async, write_text_file_async
4
+ from zrb.helper.typecheck import typechecked
5
+ from zrb.helper.util import to_capitalized_human_readable, to_kebab_case, to_snake_case
6
+ from zrb.task.task import Task
7
+
8
+ from ._common import (
9
+ get_app_frontend_routes_dir,
10
+ get_default_js_value,
11
+ get_html_input,
12
+ replace_marker,
13
+ )
14
+
15
+
16
+ @typechecked
17
+ async def inject_insert_page(
18
+ task: Task,
19
+ project_dir: str,
20
+ app_name: str,
21
+ module_name: str,
22
+ entity_name: str,
23
+ column_name: str,
24
+ column_type: str,
25
+ ):
26
+ kebab_module_name = to_kebab_case(module_name)
27
+ kebab_entity_name = to_kebab_case(entity_name)
28
+ snake_column_name = to_snake_case(column_name)
29
+ kebab_column_name = to_kebab_case(column_name)
30
+ column_caption = to_capitalized_human_readable(column_name)
31
+ file_path = os.path.join(
32
+ get_app_frontend_routes_dir(project_dir, app_name),
33
+ kebab_module_name,
34
+ kebab_entity_name,
35
+ "new",
36
+ "+page.svelte",
37
+ )
38
+ task.print_out(f"Read HTML from: {file_path}")
39
+ html_content = await read_text_file_async(file_path)
40
+ task.print_out("Add default value to insert page")
41
+ default_value = get_default_js_value(column_type)
42
+ html_content = replace_marker(
43
+ html_content,
44
+ marker="// DON'T DELETE: set field default value here",
45
+ code=f"row.{snake_column_name} = {default_value}",
46
+ )
47
+ task.print_out("Add field to insert page")
48
+ html_input = get_html_input(column_type, column_name, column_caption)
49
+ html_content = replace_marker(
50
+ html_content,
51
+ marker="<!-- DON'T DELETE: insert new field here-->",
52
+ code="\n".join(
53
+ [
54
+ '<div class="mb-4">',
55
+ f' <label class="block text-gray-700 font-bold mb-2" for="{kebab_column_name}">{column_caption}</label>', # noqa
56
+ f" {html_input}",
57
+ "</div>",
58
+ ]
59
+ ),
60
+ )
61
+ task.print_out(f"Write modified HTML to: {file_path}")
62
+ await write_text_file_async(file_path, html_content)