zrb 1.0.0b9__py3-none-any.whl → 1.1.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.
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/.coveragerc +11 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/.gitignore +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/add_column_task.py +99 -55
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/add_column_util.py +301 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/config.py +5 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_task.py +131 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +128 -5
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/gateway/view/content/my-module/my-entity.html +297 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_create_my_entity.py +53 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_delete_my_entity.py +62 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_read_my_entity.py +65 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_update_my_entity.py +61 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/gateway_subroute.py +81 -13
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/navigation_config_file.py +8 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_task.py +8 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +42 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/gateway/subroute/my_module.py +8 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/module_task_definition.py +10 -6
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/navigation_config_file.py +6 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/task.py +56 -12
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/task_util.py +10 -4
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_service.py +136 -52
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/parser.py +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/view.py +1 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/config.py +19 -8
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/3093c7336477_add_auth_tables.py +46 -43
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/8ed025bcc845_create_permissions.py +69 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_db_repository.py +5 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service.py +16 -21
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/config/navigation.py +39 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/route.py +52 -11
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/schema/navigation.py +95 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/subroute/auth.py +277 -44
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/util/auth.py +66 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/util/view.py +33 -8
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/auth/permission.html +311 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/auth/role.html +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/auth/user.html +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/error.html +4 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/login.html +67 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/logout.html +49 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/common/util.js +160 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/crud/style.css +14 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/crud/util.js +94 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/default/pico-style.css +23 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/default/script.js +44 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/default/style.css +102 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/template/default.html +73 -18
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/requirements.txt +6 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/permission.py +1 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/user.py +9 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/_util/access_token.py +19 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_create_permission.py +59 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_delete_permission.py +68 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_read_permission.py +71 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_update_permission.py +66 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/test_user_session.py +195 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_health_and_readiness.py +28 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_homepage.py +15 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_not_found_error.py +16 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test.sh +7 -0
- zrb/runner/web_route/refresh_token_api_route.py +1 -1
- zrb/runner/web_route/static/refresh-token.template.js +9 -0
- zrb/runner/web_route/static/static_route.py +1 -1
- zrb/task/base_task.py +10 -10
- zrb/util/codemod/modification_mode.py +3 -0
- zrb/util/codemod/modify_class.py +58 -0
- zrb/util/codemod/modify_class_parent.py +68 -0
- zrb/util/codemod/modify_class_property.py +128 -0
- zrb/util/codemod/modify_dict.py +75 -0
- zrb/util/codemod/modify_function.py +65 -0
- zrb/util/codemod/modify_function_call.py +68 -0
- zrb/util/codemod/modify_method.py +88 -0
- zrb/util/codemod/{prepend_code_to_module.py → modify_module.py} +2 -3
- zrb/util/file.py +3 -2
- zrb/util/load.py +13 -7
- {zrb-1.0.0b9.dist-info → zrb-1.1.0.dist-info}/METADATA +2 -2
- {zrb-1.0.0b9.dist-info → zrb-1.1.0.dist-info}/RECORD +80 -46
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/migrate.py +0 -3
- zrb/util/codemod/append_code_to_class.py +0 -35
- zrb/util/codemod/append_code_to_function.py +0 -38
- zrb/util/codemod/append_code_to_method.py +0 -55
- zrb/util/codemod/append_key_to_dict.py +0 -51
- zrb/util/codemod/append_param_to_function_call.py +0 -39
- zrb/util/codemod/prepend_parent_to_class.py +0 -38
- zrb/util/codemod/prepend_property_to_class.py +0 -55
- {zrb-1.0.0b9.dist-info → zrb-1.1.0.dist-info}/WHEEL +0 -0
- {zrb-1.0.0b9.dist-info → zrb-1.1.0.dist-info}/entry_points.txt +0 -0
@@ -2,17 +2,26 @@ import os
|
|
2
2
|
|
3
3
|
from my_app_name._zrb.config import ACTIVATE_VENV_SCRIPT, APP_DIR
|
4
4
|
from my_app_name._zrb.entity.add_entity_util import (
|
5
|
+
get_add_permission_migration_script,
|
6
|
+
get_auth_migration_version_dir,
|
7
|
+
get_existing_auth_migration_file_names,
|
8
|
+
get_existing_auth_migration_xcom_key,
|
9
|
+
get_remove_permission_migration_script,
|
10
|
+
is_gateway_navigation_config_file,
|
5
11
|
is_in_app_schema_dir,
|
6
12
|
is_in_module_entity_dir,
|
13
|
+
is_in_module_entity_test_dir,
|
7
14
|
is_module_api_client_file,
|
8
15
|
is_module_client_file,
|
9
16
|
is_module_direct_client_file,
|
10
17
|
is_module_gateway_subroute_file,
|
18
|
+
is_module_gateway_subroute_view_file,
|
11
19
|
is_module_migration_metadata_file,
|
12
20
|
is_module_route_file,
|
13
21
|
update_api_client_file,
|
14
22
|
update_client_file,
|
15
23
|
update_direct_client_file,
|
24
|
+
update_gateway_navigation_config_file,
|
16
25
|
update_gateway_subroute_file,
|
17
26
|
update_migration_metadata_file,
|
18
27
|
update_route_file,
|
@@ -42,8 +51,11 @@ from zrb import (
|
|
42
51
|
EnvFile,
|
43
52
|
Scaffolder,
|
44
53
|
Task,
|
54
|
+
Xcom,
|
45
55
|
make_task,
|
46
56
|
)
|
57
|
+
from zrb.util.codemod.modify_function import replace_function_code
|
58
|
+
from zrb.util.file import read_file, write_file
|
47
59
|
from zrb.util.string.conversion import to_snake_case
|
48
60
|
|
49
61
|
|
@@ -79,7 +91,9 @@ scaffold_my_app_name_entity = Scaffolder(
|
|
79
91
|
destination_path=APP_DIR,
|
80
92
|
transform_path={
|
81
93
|
"my_module": "{to_snake_case(ctx.input.module)}",
|
94
|
+
"my-module": "{to_kebab_case(ctx.input.module)}",
|
82
95
|
"my_entity": "{to_snake_case(ctx.input.entity)}",
|
96
|
+
"my-entity": "{to_kebab_case(ctx.input.entity)}",
|
83
97
|
},
|
84
98
|
transform_content=[
|
85
99
|
# Schema tranformation (my_app_name/schema/snake_entity_name)
|
@@ -105,6 +119,18 @@ scaffold_my_app_name_entity = Scaffolder(
|
|
105
119
|
"my-entities": "{to_kebab_case(ctx.input.plural)}",
|
106
120
|
},
|
107
121
|
),
|
122
|
+
# Test transformation (my_app_name/test/my_module/my_entity)
|
123
|
+
ContentTransformer(
|
124
|
+
name="transform-module-entity-test-dir",
|
125
|
+
match=is_in_module_entity_test_dir,
|
126
|
+
transform={
|
127
|
+
"my_module": "{to_snake_case(ctx.input.module)}",
|
128
|
+
"my_entity": "{to_snake_case(ctx.input.entity)}",
|
129
|
+
"my-entity": "{to_kebab_case(ctx.input.entity)}",
|
130
|
+
"my-entities": "{to_kebab_case(ctx.input.plural)}",
|
131
|
+
"my_column": "{to_snake_case(ctx.input.column)}",
|
132
|
+
},
|
133
|
+
),
|
108
134
|
# Add entity to migration metadata
|
109
135
|
# (my_app_name/module/snake_module_name/migration_metadata.py)
|
110
136
|
ContentTransformer(
|
@@ -146,6 +172,24 @@ scaffold_my_app_name_entity = Scaffolder(
|
|
146
172
|
match=is_module_gateway_subroute_file,
|
147
173
|
transform=update_gateway_subroute_file,
|
148
174
|
),
|
175
|
+
# Update module gateway subroute view
|
176
|
+
# (my_app_name/module/gateway/view/content/kebab-module-name/kebab-entity-name.py)
|
177
|
+
ContentTransformer(
|
178
|
+
name="transform-module-gateway-subroute-view",
|
179
|
+
match=is_module_gateway_subroute_view_file,
|
180
|
+
transform={
|
181
|
+
"My Entity": "{to_human_case(ctx.input.entity).title()}",
|
182
|
+
"my-entities": "{to_kebab_case(ctx.input.plural)}",
|
183
|
+
"My Column": "{to_human_case(ctx.input.column).title()}",
|
184
|
+
"my_column": "{to_snake_case(ctx.input.column)}",
|
185
|
+
},
|
186
|
+
),
|
187
|
+
# Register entity's page to my_app_name/gateway/config/navigation.py
|
188
|
+
ContentTransformer(
|
189
|
+
name="transform-gateway-navigation-config",
|
190
|
+
match=is_gateway_navigation_config_file,
|
191
|
+
transform=update_gateway_navigation_config_file,
|
192
|
+
),
|
149
193
|
],
|
150
194
|
retries=0,
|
151
195
|
upstream=validate_add_my_app_name_entity,
|
@@ -177,11 +221,96 @@ create_my_app_name_entity_migration = CmdTask(
|
|
177
221
|
],
|
178
222
|
)
|
179
223
|
|
224
|
+
|
225
|
+
@make_task(
|
226
|
+
name="inspect-my-app-name-auth-migration",
|
227
|
+
input=new_entity_input,
|
228
|
+
retries=0,
|
229
|
+
upstream=scaffold_my_app_name_entity,
|
230
|
+
)
|
231
|
+
def inspect_my_app_name_auth_migration(ctx: AnyContext):
|
232
|
+
"""Getting existing migration files in auth module"""
|
233
|
+
migration_file_names = get_existing_auth_migration_file_names()
|
234
|
+
xcom_key = get_existing_auth_migration_xcom_key(ctx)
|
235
|
+
if xcom_key not in ctx.xcom:
|
236
|
+
ctx.xcom[xcom_key] = Xcom([])
|
237
|
+
ctx.xcom[xcom_key].push(migration_file_names)
|
238
|
+
|
239
|
+
|
240
|
+
create_my_app_name_entity_permission = CmdTask(
|
241
|
+
name="create-my-app-name-entity-permission",
|
242
|
+
input=[
|
243
|
+
new_entity_input,
|
244
|
+
],
|
245
|
+
env=EnvFile(path=os.path.join(APP_DIR, "template.env")),
|
246
|
+
cwd=APP_DIR,
|
247
|
+
cmd=[
|
248
|
+
ACTIVATE_VENV_SCRIPT,
|
249
|
+
Cmd(lambda ctx: set_create_migration_db_url_env("auth")),
|
250
|
+
Cmd(lambda ctx: set_env("MY_APP_NAME_MODULES", "auth")),
|
251
|
+
Cmd(lambda ctx: cd_module_script("auth")),
|
252
|
+
"alembic upgrade head",
|
253
|
+
Cmd(
|
254
|
+
'alembic revision --autogenerate -m "create_{to_snake_case(ctx.input.entity)}_permission"', # noqa
|
255
|
+
),
|
256
|
+
],
|
257
|
+
render_cmd=False,
|
258
|
+
retries=0,
|
259
|
+
upstream=[
|
260
|
+
prepare_venv,
|
261
|
+
inspect_my_app_name_auth_migration,
|
262
|
+
],
|
263
|
+
)
|
264
|
+
|
265
|
+
|
266
|
+
@make_task(
|
267
|
+
name="update-my-app-name-entity-permission",
|
268
|
+
input=new_entity_input,
|
269
|
+
retries=0,
|
270
|
+
upstream=create_my_app_name_entity_permission,
|
271
|
+
)
|
272
|
+
def update_my_app_name_entity_permission(ctx: AnyContext):
|
273
|
+
xcom_key = get_existing_auth_migration_xcom_key(ctx)
|
274
|
+
existing_migration_file_names = ctx.xcom[xcom_key].pop()
|
275
|
+
current_migration_file_names = get_existing_auth_migration_file_names()
|
276
|
+
new_migration_file_names = [
|
277
|
+
file_name
|
278
|
+
for file_name in current_migration_file_names
|
279
|
+
if file_name not in existing_migration_file_names
|
280
|
+
]
|
281
|
+
if len(new_migration_file_names) == 0:
|
282
|
+
raise Exception("No migration file created")
|
283
|
+
new_migration_file_path = os.path.join(
|
284
|
+
get_auth_migration_version_dir(), new_migration_file_names[0]
|
285
|
+
)
|
286
|
+
new_migration_code = read_file(
|
287
|
+
new_migration_file_path,
|
288
|
+
{
|
289
|
+
"from alembic import op": "\n".join(
|
290
|
+
[
|
291
|
+
"from alembic import op",
|
292
|
+
"from module.auth.migration_metadata import metadata",
|
293
|
+
]
|
294
|
+
),
|
295
|
+
},
|
296
|
+
)
|
297
|
+
new_migration_code = replace_function_code(
|
298
|
+
new_migration_code, "upgrade", get_add_permission_migration_script(ctx)
|
299
|
+
)
|
300
|
+
new_migration_code = replace_function_code(
|
301
|
+
new_migration_code, "downgrade", get_remove_permission_migration_script(ctx)
|
302
|
+
)
|
303
|
+
write_file(new_migration_file_path, new_migration_code)
|
304
|
+
|
305
|
+
|
180
306
|
add_my_app_name_entity = app_create_group.add_task(
|
181
307
|
Task(
|
182
308
|
name="add-my-app-name-entity",
|
183
|
-
description="
|
184
|
-
upstream=
|
309
|
+
description="📦 Create new entity on a module",
|
310
|
+
upstream=[
|
311
|
+
create_my_app_name_entity_migration,
|
312
|
+
update_my_app_name_entity_permission,
|
313
|
+
],
|
185
314
|
successor=format_my_app_name_code,
|
186
315
|
retries=0,
|
187
316
|
),
|
@@ -3,12 +3,75 @@ import os
|
|
3
3
|
from my_app_name._zrb.config import APP_DIR
|
4
4
|
|
5
5
|
from zrb.context.any_context import AnyContext
|
6
|
-
from zrb.util.codemod.
|
7
|
-
from zrb.util.codemod.
|
8
|
-
from zrb.util.codemod.
|
9
|
-
from zrb.util.codemod.
|
6
|
+
from zrb.util.codemod.modify_class import append_code_to_class
|
7
|
+
from zrb.util.codemod.modify_class_parent import prepend_parent_class
|
8
|
+
from zrb.util.codemod.modify_function import append_code_to_function
|
9
|
+
from zrb.util.codemod.modify_module import prepend_code_to_module
|
10
10
|
from zrb.util.file import read_file, write_file
|
11
|
-
from zrb.util.string.conversion import
|
11
|
+
from zrb.util.string.conversion import (
|
12
|
+
to_human_case,
|
13
|
+
to_kebab_case,
|
14
|
+
to_pascal_case,
|
15
|
+
to_snake_case,
|
16
|
+
)
|
17
|
+
|
18
|
+
|
19
|
+
def is_gateway_navigation_config_file(ctx: AnyContext, file_path: str) -> bool:
|
20
|
+
return file_path == os.path.join(
|
21
|
+
APP_DIR, "module", "gateway", "config", "navigation.py"
|
22
|
+
)
|
23
|
+
|
24
|
+
|
25
|
+
def get_add_permission_migration_script(ctx: AnyContext) -> str:
|
26
|
+
kebab_entity_name = to_kebab_case(ctx.input.entity)
|
27
|
+
return "\n".join(
|
28
|
+
[
|
29
|
+
"op.bulk_insert(",
|
30
|
+
' metadata.tables["permissions"],',
|
31
|
+
" [",
|
32
|
+
f' {{"name": "{kebab_entity_name}:create", "description": "create {kebab_entity_name}"}},', # noqa
|
33
|
+
f' {{"name": "{kebab_entity_name}:read", "description": "read {kebab_entity_name}"}},', # noqa
|
34
|
+
f' {{"name": "{kebab_entity_name}:update", "description": "update {kebab_entity_name}"}},', # noqa
|
35
|
+
f' {{"name": "{kebab_entity_name}:delete", "description": "delete {kebab_entity_name}"}},', # noqa
|
36
|
+
" ]",
|
37
|
+
")",
|
38
|
+
]
|
39
|
+
)
|
40
|
+
|
41
|
+
|
42
|
+
def get_remove_permission_migration_script(ctx: AnyContext) -> str:
|
43
|
+
kebab_entity_name = to_kebab_case(ctx.input.entity)
|
44
|
+
return "\n".join(
|
45
|
+
[
|
46
|
+
"op.execute(",
|
47
|
+
' sa.delete(metadata.tables["permissions"])',
|
48
|
+
' .where(metadata.tables["permissions"].c.name.in_(',
|
49
|
+
f' "{kebab_entity_name}:create",',
|
50
|
+
f' "{kebab_entity_name}:read",',
|
51
|
+
f' "{kebab_entity_name}:update",',
|
52
|
+
f' "{kebab_entity_name}:delete",',
|
53
|
+
" ))",
|
54
|
+
")",
|
55
|
+
]
|
56
|
+
)
|
57
|
+
|
58
|
+
|
59
|
+
def get_auth_migration_version_dir() -> str:
|
60
|
+
return os.path.join(APP_DIR, "module", "auth", "migration", "versions")
|
61
|
+
|
62
|
+
|
63
|
+
def get_existing_auth_migration_file_names() -> list[str]:
|
64
|
+
migration_version_dir = get_auth_migration_version_dir()
|
65
|
+
return [
|
66
|
+
file_name
|
67
|
+
for file_name in os.listdir(migration_version_dir)
|
68
|
+
if file_name.endswith(".py")
|
69
|
+
]
|
70
|
+
|
71
|
+
|
72
|
+
def get_existing_auth_migration_xcom_key(ctx: AnyContext) -> str:
|
73
|
+
snake_entity_name = to_snake_case(ctx.input.entity)
|
74
|
+
return f"existing_my_app_name_auth_{snake_entity_name}_migration"
|
12
75
|
|
13
76
|
|
14
77
|
def is_in_app_schema_dir(ctx: AnyContext, file_path: str) -> bool:
|
@@ -17,6 +80,17 @@ def is_in_app_schema_dir(ctx: AnyContext, file_path: str) -> bool:
|
|
17
80
|
)
|
18
81
|
|
19
82
|
|
83
|
+
def is_in_module_entity_test_dir(ctx: AnyContext, file_path: str) -> bool:
|
84
|
+
return file_path.startswith(
|
85
|
+
os.path.join(
|
86
|
+
APP_DIR,
|
87
|
+
"test",
|
88
|
+
to_snake_case(ctx.input.module),
|
89
|
+
to_snake_case(ctx.input.entity),
|
90
|
+
)
|
91
|
+
)
|
92
|
+
|
93
|
+
|
20
94
|
def is_in_module_entity_dir(ctx: AnyContext, file_path: str) -> bool:
|
21
95
|
return file_path.startswith(
|
22
96
|
os.path.join(
|
@@ -93,6 +167,19 @@ def is_module_gateway_subroute_file(ctx: AnyContext, file_path: str) -> bool:
|
|
93
167
|
return file_path == module_gateway_subroute_file
|
94
168
|
|
95
169
|
|
170
|
+
def is_module_gateway_subroute_view_file(ctx: AnyContext, file_path: str) -> bool:
|
171
|
+
module_gateway_subroute_file = os.path.join(
|
172
|
+
APP_DIR,
|
173
|
+
"module",
|
174
|
+
"gateway",
|
175
|
+
"view",
|
176
|
+
"content",
|
177
|
+
f"{to_kebab_case(ctx.input.module)}",
|
178
|
+
f"{to_kebab_case(ctx.input.entity)}.html",
|
179
|
+
)
|
180
|
+
return file_path == module_gateway_subroute_file
|
181
|
+
|
182
|
+
|
96
183
|
def update_migration_metadata_file(ctx: AnyContext, migration_metadata_file_path: str):
|
97
184
|
app_name = os.path.basename(APP_DIR)
|
98
185
|
existing_migration_metadata_code = read_file(migration_metadata_file_path)
|
@@ -255,7 +342,9 @@ def update_route_file(ctx: AnyContext, route_file_path: str):
|
|
255
342
|
|
256
343
|
def update_gateway_subroute_file(ctx: AnyContext, module_gateway_subroute_path: str):
|
257
344
|
snake_module_name = to_snake_case(ctx.input.module)
|
345
|
+
kebab_module_name = to_kebab_case(ctx.input.module)
|
258
346
|
snake_entity_name = to_snake_case(ctx.input.entity)
|
347
|
+
kebab_entity_name = to_kebab_case(ctx.input.entity)
|
259
348
|
snake_plural_entity_name = to_snake_case(ctx.input.plural)
|
260
349
|
kebab_plural_entity_name = to_kebab_case(ctx.input.plural)
|
261
350
|
pascal_entity_name = to_pascal_case(ctx.input.entity)
|
@@ -278,7 +367,9 @@ def update_gateway_subroute_file(ctx: AnyContext, module_gateway_subroute_path:
|
|
278
367
|
),
|
279
368
|
replace_map={
|
280
369
|
"my_module": snake_module_name,
|
370
|
+
"my-module": kebab_module_name,
|
281
371
|
"my_entity": snake_entity_name,
|
372
|
+
"my-entity": kebab_entity_name,
|
282
373
|
"my_entities": snake_plural_entity_name,
|
283
374
|
"my-entities": kebab_plural_entity_name,
|
284
375
|
"MyEntity": pascal_entity_name,
|
@@ -319,3 +410,35 @@ def _get_import_schema_for_gateway_subroute_code(
|
|
319
410
|
if new_code in existing_code:
|
320
411
|
return None
|
321
412
|
return new_code
|
413
|
+
|
414
|
+
|
415
|
+
def update_gateway_navigation_config_file(
|
416
|
+
ctx: AnyContext, gateway_navigation_config_file_path: str
|
417
|
+
):
|
418
|
+
existing_gateway_navigation_config_code = read_file(
|
419
|
+
gateway_navigation_config_file_path
|
420
|
+
)
|
421
|
+
snake_module_name = to_snake_case(ctx.input.module)
|
422
|
+
kebab_module_name = to_kebab_case(ctx.input.module)
|
423
|
+
kebab_entity_name = to_kebab_case(ctx.input.entity)
|
424
|
+
human_entity_name = to_human_case(ctx.input.entity)
|
425
|
+
kebab_plural_name = to_kebab_case(ctx.input.plural)
|
426
|
+
new_navigation_config_code = read_file(
|
427
|
+
file_path=os.path.join(
|
428
|
+
os.path.dirname(__file__), "template", "navigation_config_file.py"
|
429
|
+
),
|
430
|
+
replace_map={
|
431
|
+
"my_module": snake_module_name,
|
432
|
+
"my-module": kebab_module_name,
|
433
|
+
"my-entity": kebab_entity_name,
|
434
|
+
"My Entity": human_entity_name.title(),
|
435
|
+
"my-entities": kebab_plural_name,
|
436
|
+
},
|
437
|
+
).strip()
|
438
|
+
write_file(
|
439
|
+
file_path=gateway_navigation_config_file_path,
|
440
|
+
content=[
|
441
|
+
existing_gateway_navigation_config_code,
|
442
|
+
new_navigation_config_code,
|
443
|
+
],
|
444
|
+
)
|
@@ -0,0 +1,297 @@
|
|
1
|
+
<link rel="stylesheet" href="/static/crud/style.css">
|
2
|
+
|
3
|
+
<main class="container">
|
4
|
+
<article>
|
5
|
+
<h1>My Entity</h1>
|
6
|
+
|
7
|
+
<fieldset id="crud-table-fieldset" role="group" class="grid">
|
8
|
+
<input id="crud-filter" onchange="applySearch()" placeholder="🔍 Filter" aria-label="Search" />
|
9
|
+
<button onclick="applySearch()">🔍 Search</button>
|
10
|
+
{% if allow_create %}
|
11
|
+
<button class="contrast" onclick="showCreateForm(event)">➕ Add</button>
|
12
|
+
{% endif %}
|
13
|
+
</fieldset>
|
14
|
+
<div id="crud-table-container">
|
15
|
+
<table id="crud-table" class="striped">
|
16
|
+
<thead>
|
17
|
+
<tr>
|
18
|
+
<th scope="col">ID</th>
|
19
|
+
<th scope="col">My Column</th>
|
20
|
+
<!-- Update this -->
|
21
|
+
{% if allow_update or allow_delete %}
|
22
|
+
<th scope="col">Actions</th>
|
23
|
+
{% endif %}
|
24
|
+
</tr>
|
25
|
+
</thead>
|
26
|
+
<tbody></tbody>
|
27
|
+
</table>
|
28
|
+
</div>
|
29
|
+
<div id="crud-pagination"></div>
|
30
|
+
|
31
|
+
{% if allow_create %}
|
32
|
+
<dialog id="crud-create-form-dialog">
|
33
|
+
<article>
|
34
|
+
<h2>New My Entity</h2>
|
35
|
+
<form id="crud-create-form">
|
36
|
+
<label>
|
37
|
+
My Column:
|
38
|
+
<input type="text" name="my_column" required>
|
39
|
+
</label>
|
40
|
+
<!-- Update this -->
|
41
|
+
<footer>
|
42
|
+
<button onclick="createRow(event)">➕ Save</button>
|
43
|
+
<button class="secondary" onclick="hideCreateForm(event)">❌ Cancel</button>
|
44
|
+
</footer>
|
45
|
+
</form>
|
46
|
+
</article>
|
47
|
+
</dialog>
|
48
|
+
{% endif %}
|
49
|
+
|
50
|
+
{% if allow_update %}
|
51
|
+
<dialog id="crud-update-form-dialog">
|
52
|
+
<article>
|
53
|
+
<h2>Update My Entity</h2>
|
54
|
+
<form id="crud-update-form">
|
55
|
+
<label>
|
56
|
+
My Column:
|
57
|
+
<input type="text" name="my_column" required>
|
58
|
+
</label>
|
59
|
+
<!-- Update this -->
|
60
|
+
<footer>
|
61
|
+
<button onclick="updateRow(event)">✏️ Save</button>
|
62
|
+
<button class="secondary" onclick="hideUpdateForm(event)">❌ Cancel</button>
|
63
|
+
</footer>
|
64
|
+
</form>
|
65
|
+
</article>
|
66
|
+
</dialog>
|
67
|
+
{% endif %}
|
68
|
+
|
69
|
+
{% if allow_delete %}
|
70
|
+
<dialog id="crud-delete-form-dialog">
|
71
|
+
<article>
|
72
|
+
<h2>Delete My Entity</h2>
|
73
|
+
<form id="crud-delete-form">
|
74
|
+
<label>
|
75
|
+
My Column:
|
76
|
+
<input type="text" name="my_column" readonly>
|
77
|
+
</label>
|
78
|
+
<!-- Update this -->
|
79
|
+
<footer>
|
80
|
+
<button class="secondary" onclick="hideDeleteForm()">❌ Cancel</button>
|
81
|
+
<button onclick="deleteRow()">🗑️ Delete</button>
|
82
|
+
</footer>
|
83
|
+
</form>
|
84
|
+
</article>
|
85
|
+
</dialog>
|
86
|
+
{% endif %}
|
87
|
+
|
88
|
+
<dialog id="crud-alert-dialog">
|
89
|
+
<article>
|
90
|
+
<h2 id="crud-alert-title">Error</h2>
|
91
|
+
<pre id="crud-alert-message"></pre>
|
92
|
+
<footer>
|
93
|
+
<button onclick="hideAlert(event)">Close</button>
|
94
|
+
</footer>
|
95
|
+
</article>
|
96
|
+
</dialog>
|
97
|
+
|
98
|
+
</article>
|
99
|
+
</main>
|
100
|
+
|
101
|
+
<script src="/static/crud/util.js"></script>
|
102
|
+
<script>
|
103
|
+
const apiUrl = "/api/v1/my-entities";
|
104
|
+
const crudState = {
|
105
|
+
pageSize: {{page_size | tojson}},
|
106
|
+
currentPage: {{page | tojson}},
|
107
|
+
sort: {{sort | tojson}},
|
108
|
+
filter: {{filter | tojson}},
|
109
|
+
allowCreate: {{allow_create | tojson}},
|
110
|
+
allowUpdate: {{allow_update | tojson}},
|
111
|
+
allowDelete: {{allow_delete | tojson}},
|
112
|
+
updatedRowId: null,
|
113
|
+
deletedRowId: null,
|
114
|
+
};
|
115
|
+
|
116
|
+
async function applySearch() {
|
117
|
+
const filterInput = document.getElementById("crud-filter");
|
118
|
+
crudState.filter = filterInput.value;
|
119
|
+
return await fetchRows(crudState.currentPage);
|
120
|
+
}
|
121
|
+
|
122
|
+
async function fetchRows(page = null) {
|
123
|
+
try {
|
124
|
+
if (typeof page !== 'undefined' && page !== null) {
|
125
|
+
crudState.currentPage = page;
|
126
|
+
}
|
127
|
+
const defaultSearchColumn = "my_column"
|
128
|
+
// update address bar
|
129
|
+
const searchParam = CRUD_UTIL.getSearchParam(crudState, defaultSearchColumn, false);
|
130
|
+
const newUrl = `${window.location.pathname}?${searchParam}`;
|
131
|
+
window.history.pushState({ path: newUrl }, "", newUrl);
|
132
|
+
// update table and pagination
|
133
|
+
const apiSearchParam = CRUD_UTIL.getSearchParam(crudState, defaultSearchColumn, true);
|
134
|
+
const result = await UTIL.fetchAPI(
|
135
|
+
`${apiUrl}?${apiSearchParam}`, { method: "GET" }
|
136
|
+
);
|
137
|
+
renderRows(result.data);
|
138
|
+
const crudPagination = document.getElementById("crud-pagination");
|
139
|
+
CRUD_UTIL.renderPagination(
|
140
|
+
crudPagination, crudState, result.count, "fetchRows"
|
141
|
+
);
|
142
|
+
} catch (error) {
|
143
|
+
console.error("Error fetching items:", error);
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
function renderRows(rows) {
|
148
|
+
const tableBody = document.querySelector("#crud-table tbody");
|
149
|
+
tableBody.innerHTML = "";
|
150
|
+
rows.forEach(row => {
|
151
|
+
let rowComponent = getRowComponents(row);
|
152
|
+
actionColumn = "";
|
153
|
+
if (crudState.allowUpdate) {
|
154
|
+
actionColumn += `<button class="contrast" onclick="showUpdateForm('${row.id}')">✏️ Edit</button>`;
|
155
|
+
}
|
156
|
+
if (crudState.allowDelete) {
|
157
|
+
actionColumn += `<button class="secondary" onclick="showDeleteForm('${row.id}')">🗑️ Delete</button>`;
|
158
|
+
}
|
159
|
+
if (crudState.allowUpdate || crudState.allowDelete) {
|
160
|
+
actionColumn = `<td><fieldset class="grid" role="group">${actionColumn}</fieldset></td>`;
|
161
|
+
}
|
162
|
+
tableBody.innerHTML += `<tr>${rowComponent.join('')}${actionColumn}</tr>`;
|
163
|
+
});
|
164
|
+
}
|
165
|
+
|
166
|
+
function getRowComponents(row) {
|
167
|
+
let rowComponents = [];
|
168
|
+
rowComponents.push(`<td>${row.id}</td>`);
|
169
|
+
rowComponents.push(`<td>${row.my_column}</td>`);
|
170
|
+
// Update this
|
171
|
+
return rowComponents;
|
172
|
+
}
|
173
|
+
|
174
|
+
{% if allow_create %}
|
175
|
+
async function showCreateForm(id) {
|
176
|
+
const createFormDialog = document.getElementById("crud-create-form-dialog");
|
177
|
+
const createForm = document.getElementById("crud-create-form");
|
178
|
+
UTIL.clearFormData(createForm);
|
179
|
+
createFormDialog.showModal();
|
180
|
+
}
|
181
|
+
|
182
|
+
async function createRow(event = null) {
|
183
|
+
if (event != null) {
|
184
|
+
event.preventDefault();
|
185
|
+
}
|
186
|
+
try {
|
187
|
+
const createForm = document.getElementById("crud-create-form");
|
188
|
+
const formData = JSON.stringify(UTIL.getFormData(createForm));
|
189
|
+
await UTIL.fetchAPI(apiUrl, {method: "POST", body: formData});
|
190
|
+
await fetchRows();
|
191
|
+
hideCreateForm();
|
192
|
+
} catch(error) {
|
193
|
+
showAlert("Create My Entity Error", error);
|
194
|
+
}
|
195
|
+
}
|
196
|
+
|
197
|
+
function hideCreateForm(event = null) {
|
198
|
+
if (event != null) {
|
199
|
+
event.preventDefault();
|
200
|
+
}
|
201
|
+
const createFormDialog = document.getElementById("crud-create-form-dialog");
|
202
|
+
createFormDialog.close();
|
203
|
+
}
|
204
|
+
{% endif %}
|
205
|
+
|
206
|
+
{% if allow_update %}
|
207
|
+
async function showUpdateForm(id) {
|
208
|
+
crudState.updatedRowId = id;
|
209
|
+
const updateFormDialog = document.getElementById("crud-update-form-dialog");
|
210
|
+
const updateForm = document.getElementById("crud-update-form");
|
211
|
+
result = await UTIL.fetchAPI(`${apiUrl}/${id}`, { method: "GET" });
|
212
|
+
UTIL.setFormData(updateForm, result);
|
213
|
+
updateFormDialog.showModal();
|
214
|
+
}
|
215
|
+
|
216
|
+
async function updateRow(event = null) {
|
217
|
+
if (event != null) {
|
218
|
+
event.preventDefault();
|
219
|
+
}
|
220
|
+
try {
|
221
|
+
const updateForm = document.getElementById("crud-update-form");
|
222
|
+
const formData = JSON.stringify(UTIL.getFormData(updateForm));
|
223
|
+
await UTIL.fetchAPI(
|
224
|
+
`${apiUrl}/${crudState.updatedRowId}`, {method: "PUT", body: formData},
|
225
|
+
);
|
226
|
+
await fetchRows();
|
227
|
+
hideUpdateForm();
|
228
|
+
} catch(error) {
|
229
|
+
showAlert("Update My Entity Error", error);
|
230
|
+
}
|
231
|
+
}
|
232
|
+
|
233
|
+
function hideUpdateForm(event = null) {
|
234
|
+
if (event != null) {
|
235
|
+
event.preventDefault();
|
236
|
+
}
|
237
|
+
const updateFormDialog = document.getElementById("crud-update-form-dialog");
|
238
|
+
updateFormDialog.close();
|
239
|
+
}
|
240
|
+
{% endif %}
|
241
|
+
|
242
|
+
{% if allow_delete %}
|
243
|
+
async function showDeleteForm(id) {
|
244
|
+
crudState.deletedRowId = id;
|
245
|
+
const deleteFormDialog = document.getElementById("crud-delete-form-dialog");
|
246
|
+
const deleteForm = document.getElementById("crud-delete-form");
|
247
|
+
result = await UTIL.fetchAPI(`${apiUrl}/${id}`, { method: "GET" });
|
248
|
+
UTIL.setFormData(deleteForm, result);
|
249
|
+
deleteFormDialog.showModal();
|
250
|
+
}
|
251
|
+
|
252
|
+
async function deleteRow(event = null) {
|
253
|
+
if (event != null) {
|
254
|
+
event.preventDefault();
|
255
|
+
}
|
256
|
+
try {
|
257
|
+
await UTIL.fetchAPI(`${apiUrl}/${crudState.deletedRowId}`, {method: "DELETE",});
|
258
|
+
await fetchRows();
|
259
|
+
hideDeleteForm();
|
260
|
+
} catch(error) {
|
261
|
+
showAlert("Delete My Entity Error", error);
|
262
|
+
}
|
263
|
+
}
|
264
|
+
|
265
|
+
function hideDeleteForm(event = null) {
|
266
|
+
if (event != null) {
|
267
|
+
event.preventDefault();
|
268
|
+
}
|
269
|
+
const deleteFormDialog = document.getElementById("crud-delete-form-dialog");
|
270
|
+
deleteFormDialog.close();
|
271
|
+
}
|
272
|
+
{% endif %}
|
273
|
+
|
274
|
+
function showAlert(title, error) {
|
275
|
+
const alertDialog = document.getElementById("crud-alert-dialog");
|
276
|
+
const alertTitle = document.getElementById("crud-alert-title");
|
277
|
+
const alertMessage = document.getElementById("crud-alert-message");
|
278
|
+
const errorMessage = error.message ? error.message : String(error);
|
279
|
+
alertTitle.textContent = title;
|
280
|
+
alertMessage.textContent = errorMessage;
|
281
|
+
alertDialog.showModal();
|
282
|
+
}
|
283
|
+
|
284
|
+
function hideAlert(event = null) {
|
285
|
+
if (event != null) {
|
286
|
+
event.preventDefault();
|
287
|
+
}
|
288
|
+
const alertDialog = document.getElementById("crud-alert-dialog");
|
289
|
+
alertDialog.close();
|
290
|
+
}
|
291
|
+
|
292
|
+
document.addEventListener("DOMContentLoaded", () => {
|
293
|
+
const filterInput = document.getElementById("crud-filter");
|
294
|
+
filterInput.value = crudState.filter;
|
295
|
+
fetchRows(crudState.currentPage);
|
296
|
+
});
|
297
|
+
</script>
|